Ostatnio musiałem przekształcić tekst podwójny w szereg, a potem go odzyskać. Wartość wydaje się nie być równoważna:
double d1 = 0.84551240822557006;
string s = d1.ToString("R");
double d2 = double.Parse(s);
bool s1 = d1 == d2;
// -> s1 is False
Ale zgodnie z MSDN: Standardowe ciągi formatu liczbowego opcja „R” ma gwarantować bezpieczeństwo w obie strony.
Specyfikator formatu w obie strony („R”) służy do zapewnienia, że wartość liczbowa przekonwertowana na ciąg zostanie przeanalizowana z powrotem do tej samej wartości liczbowej
Dlaczego się to stało?
Odpowiedzi:
Znalazłem błąd.
.NET wykonuje następujące czynności
clr\src\vm\comnumber.cpp
:DoubleToNumber
jest dość proste - po prostu wywołuje_ecvt
, co jest w środowisku uruchomieniowym C:Okazuje się, że
_ecvt
zwraca ciąg845512408225570
.Zwróć uwagę na końcowe zero? Okazuje się, że robi różnicę!
Kiedy zero jest obecne, wynik faktycznie analizuje z powrotem
0.84551240822557006
, który jest twoim pierwotnym numerem - więc porównuje równy, a zatem zwracanych jest tylko 15 cyfr.Jeśli jednak obetnę ciąg o tym zeru do
84551240822557
, to wrócę0.84551240822556994
, co nie jest twoim pierwotnym numerem, a zatem zwróci 17 cyfr.Dowód: uruchom następujący kod 64-bitowy (większość z nich wyodrębniłem z interfejsu Microsoft Shared Source CLI 2.0) w swoim debuggerze i sprawdź
v
na końcumain
:źródło
+1
. Ten kod pochodzi z shared-source-cli-2.0, prawda? To jedyna myśl, jaką znalazłem.Wydaje mi się, że to po prostu błąd. Twoje oczekiwania są całkowicie uzasadnione. Odtworzyłem go przy użyciu .NET 4.5.1 (x64), uruchamiając następującą aplikację konsolową, która korzysta z mojej
DoubleConverter
klasy.DoubleConverter.ToExactString
pokazuje dokładną wartość reprezentowaną przezdouble
:Wyniki w .NET:
Wyniki w Mono 3.3.0:
Jeśli ręcznie określisz ciąg z Mono (który zawiera „006” na końcu), .NET przeanalizuje go z powrotem do oryginalnej wartości. Wygląda na to, że problemem jest
ToString("R")
obsługa, a nie parsowanie.Jak zauważono w innych komentarzach, wygląda na to, że jest to specyficzne dla działania w CLR x64. Jeśli skompilujesz i uruchomisz powyższy kod kierujący na x86, wszystko będzie w porządku:
... masz takie same wyniki jak w przypadku Mono. Byłoby ciekawie wiedzieć, czy błąd pojawia się w RyuJIT - sam tego nie mam. W szczególności mogę sobie wyobrazić, że jest to błąd JIT, lub całkiem możliwe, że istnieją zupełnie różne implementacje elementów wewnętrznych
double.ToString
opartych na architekturze.Sugeruję zgłoszenie błędu na stronie http://connect.microsoft.com
źródło
ToString()
? Próbowałem zastąpić wartość zakodowaną na stałerand.NextDouble()
i nie było problemu.ToString("R")
konwersji. SpróbujToString("G32")
zauważyć, że drukuje prawidłową wartość.Ostatnio próbuję rozwiązać ten problem . Jak wskazano w kodzie , double.ToString („R”) ma następującą logikę:
W takim przypadku double.ToString („R”) nieprawidłowo wybrał wynik z dokładnością do 15, więc błąd się zdarza. Dokument MSDN zawiera oficjalne obejście:
Tak więc, chyba że problem zostanie rozwiązany, należy użyć funkcji double.ToString („G17”) w celu przejścia w obie strony.
Aktualizacja : teraz istnieje konkretny problem do śledzenia tego błędu.
źródło