Przeanalizuj liczbę z notacji wykładniczej

85

Muszę przeanalizować ciąg „1.2345E-02” (liczba wyrażona w notacji wykładniczej) do dziesiętnego typu danych, ale Decimal.Parse("1.2345E-02")po prostu zgłasza błąd

Jimbo
źródło

Odpowiedzi:

170

Jest to liczba zmiennoprzecinkowa, musisz jej powiedzieć, że:

decimal d = Decimal.Parse("1.2345E-02", System.Globalization.NumberStyles.Float);
Hans Passant
źródło
49

Działa, jeśli określisz NumberStyles.Float:

decimal x = decimal.Parse("1.2345E-02", NumberStyles.Float);
Console.WriteLine(x); // Prints 0.012345

Nie jestem do końca pewien, dlaczego nie jest to obsługiwane domyślnie - domyślnie NumberStyles.Numberużywa się stylów AllowLeadingWhite, AllowTrailingWhite, AllowLeadingSign, AllowTrailingSign, AllowDecimalPoint i AllowThousands. Prawdopodobnie jest to związane z wydajnością; Przypuszczam, że określenie wykładnika jest stosunkowo rzadkie.

Jon Skeet
źródło
Staram się, aby to działało z podwójnym, ale wydaje się, że nie. Nie wiem, dlaczego nie mógł…?
Sty
@JanT: Nie mając więcej informacji niż „nie będzie” i „nie może”, nie mogę więcej pomóc. Proponuję zadać nowe, bardziej szczegółowe pytanie, pokazujące, czego próbowałeś i dokładnie, co się stało.
Jon Skeet,
Próbowałem uruchomić kod jak w twojej odpowiedzi, ale zamiast dziesiętnego użyłem podwójnego. Ale już znalazłem obejście. Pozdrawiam
JanT
1
@JanT Byłoby miło, gdybyś mógł podzielić się swoim obejściem. Mam dokładnie ten sam problem i przydałoby się informacje. Dzięki!
Rick Glimmer
@RickGlimmer: Nie jestem pewien, skąd wiesz, że twój problem jest dokładnie taki sam jak JanT, biorąc pod uwagę, że nigdy nie podali szczegółów tego, co próbowali zrobić. Wymiana decimalz doublemoim kod działa dobrze dla mnie, tak jak ja go spodziewać. Gdybyś mógł podać szczegóły tego, co próbujesz, kod, którego używasz, i wynik, byłoby znacznie łatwiej pomóc.
Jon Skeet
35

Oprócz określenia NumberStyles zalecałbym użycie funkcji decimal.TryParse, takiej jak:

decimal result;
if( !decimal.TryParse("1.2345E-02", NumberStyles.Any, CultureInfo.InvariantCulture, out result) )
{
     // do something in case it fails?
}

Jako alternatywa dla NumberStyles, możesz użyć określonego zestawu, jeśli masz pewność co do swoich formatów. na przykład:

NumberStyles.AllowExponent | NumberStyles.Float
Sverrir Sigmundarson
źródło
1
Ale nie jest konieczne używanie Float z AllowExponent, ponieważ Float = AllowLeadingWhite | AllowTrailingWhite | AllowLeadingSign | AllowDecimalPoint | AllowExponent
Lukáš Kmoch
@ LukášKmoch Rzeczywiście masz rację. Siła przyzwyczajenia, podobnie jak inne (poza Any), nie obejmuje jej. Nie powinno jednak zaszkodzić wykonaniu dodatkowej sali operacyjnej.
Sverrir Sigmundarson
13
decimal d = Decimal.Parse("1.2345E-02", System.Globalization.NumberStyles.Float);
Mitch Wheat
źródło
8

Zachowaj ostrożność co do wybranej odpowiedzi: istnieje podtemat określający System.Globalization.NumberStyles.Float w Decimal.Parse, który może prowadzić do wyjątku System.FormatException, ponieważ Twój system może oczekiwać na liczbę sformatowaną za pomocą „,” zamiast „”.

Na przykład w notacji francuskiej „1,2345E-02” jest niepoprawne, należy najpierw przekonwertować je na „1,2345E-02”.

Podsumowując, użyj czegoś podobnego do:

Decimal.Parse(valueString.Replace('.',','), System.Globalization.NumberStyles.Float);
KwentRell
źródło
1
Masz całkowitą rację. Nie rozumiem, dlaczego nikt inny o tym nie wspomniał.
Carles Alcolea,
10
Lepiej wykorzystaj CultureInfo.InvariantCulture jako trzeci parametr Parse
Andriy Kozachuk
3

Zauważyłem, że przekazanie NumberStyles.Float, w niektórych przypadkach, zmienia reguły, według których przetwarzany jest ciąg i skutkuje innym wyjściem z NumberStyles.Number(domyślne reguły używane przez decimal.Parse).

Na przykład poniższy kod wygeneruje FormatExceptionw moim komputerze:

CultureInfo culture = new CultureInfo("");
culture.NumberFormat.NumberDecimalDigits = 2;
culture.NumberFormat.NumberDecimalSeparator = ".";
culture.NumberFormat.NumberGroupSeparator = ",";
Decimal.Parse("1,234.5", NumberStyles.Float, culture); // FormatException thrown here

Zalecam użycie danych wejściowych NumberStyles.Number | NumberStyles.AllowExponent, ponieważ pozwoli to na liczby wykładnicze i nadal będzie przetwarzać ciąg zgodnie z decimalregułami.

CultureInfo culture = new CultureInfo("");
culture.NumberFormat.NumberDecimalDigits = 2;
culture.NumberFormat.NumberDecimalSeparator = ".";
culture.NumberFormat.NumberGroupSeparator = ",";
Decimal.Parse("1,234.5",NumberStyles.Number | NumberStyles.AllowExponent, culture); // Does not generate a FormatException

Aby odpowiedzieć na pytanie autora, właściwą odpowiedzią powinno być:

decimal x = decimal.Parse("1.2345E-02", NumberStyles.Number | NumberStyles.AllowExponent);
Console.WriteLine(x);
bastos.sergio
źródło
1

Ostrzeżenie dotyczące używania NumberStyles.

„6,33E + 03” jest konwertowane zgodnie z oczekiwaniami na 6330. W języku niemieckim kropki dziesiętne są reprezentowane przecinkami, ale 6,33E + 03 konwertuje do 633000! To problem dla moich klientów, ponieważ kultura generująca dane nie jest znana i może być inna niż kultura operująca na danych. W moim przypadku zawsze używam notacji naukowej, więc zawsze mogę zamienić przecinek na przecinek przed analizą, ale jeśli pracujesz z dowolnymi liczbami, takimi jak ładnie sformatowane liczby, takie jak 1,234,567, to podejście nie działa.

David Kitzinger
źródło
0

Nie musisz zastępować kropek (odpowiednio przecinków), wystarczy określić wejściowy IFormatProvider:

float d = Single.Parse("1.27315", System.Globalization.NumberStyles.Float, new CultureInfo("en-US"));
float d = Single.Parse("1,27315", System.Globalization.NumberStyles.Float, new CultureInfo("de-DE"));
śmiertelny
źródło
0

Jeśli chcesz sprawdzić i przekonwertować wartość wykładnika, użyj tego

string val = "1.2345E-02";
double dummy;
bool hasExponential = (val.Contains("E") || val.Contains("e")) && double.TryParse(val, out dummy);
if (hasExponential)
{
    decimal d = decimal.Parse(val, NumberStyles.Float);
}

Mam nadzieję, że to komuś pomoże.

Baran
źródło