Vor kurzem musste ich ein Double in Text serialisieren und es dann zurückbekommen. Der Wert scheint nicht gleichwertig zu sein:
double d1 = 0.84551240822557006;
string s = d1.ToString("R");
double d2 = double.Parse(s);
bool s1 = d1 == d2;
// -> s1 is False
Laut MSDN: Standard Numeric Format Strings soll die Option "R" die Sicherheit beim Hin- und Rückflug gewährleisten.
Der Formatbezeichner für den Roundtrip ("R") wird verwendet, um sicherzustellen, dass ein numerischer Wert, der in eine Zeichenfolge konvertiert wird, wieder in denselben numerischen Wert analysiert wird
Warum ist das passiert?
Antworten:
Ich habe den Fehler gefunden.
.NET führt Folgendes aus
clr\src\vm\comnumber.cpp
:DoubleToNumber
ist ziemlich einfach - es ruft nur auf_ecvt
, was in der C-Laufzeit ist:Es stellt sich heraus, dass
_ecvt
die Zeichenfolge zurückgegeben wird845512408225570
.Beachten Sie die nachfolgende Null? Es stellt sich heraus, dass das den Unterschied macht!
Wenn die Null vorhanden ist, wird das Ergebnis tatsächlich auf
0.84551240822557006
Ihre ursprüngliche Zahl zurückgeführt. Es wird also gleich verglichen, und daher werden nur 15 Ziffern zurückgegeben.Wenn ich jedoch die Zeichenfolge bei dieser Null auf kürze
84551240822557
, erhalte ich zurück0.84551240822556994
, was nicht Ihre ursprüngliche Nummer ist, und daher würde es 17 Ziffern zurückgeben.Beweis: Führen Sie den folgenden 64-Bit-Code (von dem ich den größten Teil aus der Microsoft Shared Source CLI 2.0 extrahiert habe) in Ihrem Debugger aus und überprüfen Sie ihn
v
am Ende vonmain
:quelle
+1
. Dieser Code stammt von Shared-Source-Cli-2.0, oder? Dies ist der einzige Gedanke, den ich gefunden habe.Es scheint mir, dass dies einfach ein Fehler ist. Ihre Erwartungen sind völlig vernünftig. Ich habe es mit .NET 4.5.1 (x64) reproduziert und die folgende Konsolen-App ausgeführt, die meine
DoubleConverter
Klasse verwendet.DoubleConverter.ToExactString
zeigt den genauen Wert, dargestellt durch adouble
:Ergebnisse in .NET:
Ergebnisse in Mono 3.3.0:
Wenn Sie die Zeichenfolge von Mono (die am Ende "006" enthält) manuell angeben, analysiert .NET diese Zeichenfolge auf den ursprünglichen Wert zurück. Es sieht so aus, als ob das Problem eher in der
ToString("R")
Handhabung als in der Analyse liegt.Wie in anderen Kommentaren erwähnt, scheint dies spezifisch für die Ausführung unter der x64-CLR zu sein. Wenn Sie den obigen Code für x86 kompilieren und ausführen, ist dies in Ordnung:
... Sie erhalten die gleichen Ergebnisse wie mit Mono. Es wäre interessant zu wissen, ob der Fehler unter RyuJIT auftritt - das habe ich momentan selbst nicht installiert. Insbesondere kann ich mir vorstellen, dass dies möglicherweise ein JIT-Fehler ist, oder es ist durchaus möglich, dass es ganz unterschiedliche Implementierungen der
double.ToString
auf Architektur basierenden Interna gibt .Ich schlage vor, Sie melden einen Fehler unter http://connect.microsoft.com
quelle
ToString()
? Als ich versuchte, den fest codierten Wert durch zu ersetzen,rand.NextDouble()
gab es kein Problem.ToString("R")
Konvertierung. Versuchen Sie zuToString("G32")
bemerken, dass der richtige Wert gedruckt wird.Vor kurzem versuche ich, dieses Problem zu beheben . Wie im Code hervorgehoben , hat der double.ToString ("R") folgende Logik:
In diesem Fall hat double.ToString ("R") das Ergebnis fälschlicherweise mit einer Genauigkeit von 15 ausgewählt, damit der Fehler auftritt. Es gibt eine offizielle Problemumgehung im MSDN-Dokument:
Wenn dieses Problem nicht behoben ist, müssen Sie double.ToString ("G17") für Roundtrips verwenden.
Update : Jetzt gibt es ein spezielles Problem , um diesen Fehler zu verfolgen.
quelle