2015-10-15 12 views
23

Wenn ich laufen die AnweisungÄnderungen an Math.exp oder doppelte Ausführung in .net 4.5.2

Math.Exp(113.62826122038274).ToString("R") 

auf einer Maschine mit .net 4.5.1 installiert ist, dann bekomme ich die Antwort

2.2290860617259248E+49 

Allerdings, wenn ich den gleichen Befehl auf einem Computer mit .NET Framework 4.5.2 installiert laufen, dann bekomme ich die Antwort

2.2290860617259246E+49 

(dh die endgültige d igit Änderungen)

Ich weiß, dass dies in rein numerischen Begriffen weitgehend unbedeutend ist, aber kennt jemand irgendwelche Änderungen, die in .net 4.5.2 vorgenommen wurden, die die Änderung erklären würde?

(ich es vorziehen, nicht ein Ergebnis zum anderen, ich bin nur daran interessiert zu verstehen, warum sie sich geändert hat) Ich

Wenn ich Ausgang

The input in roundtrip format 
The input converted to a long via BitConverter.DoubleToInt64Bits 
Math.Exp in roundtrip format 
Math.Exp converted to a long via BitConverter.DoubleToInt64Bits 

dann auf 4.5.1 bekommen

113.62826122038274 
4637696294982039780 
2.2290860617259248E+49 
5345351685623826106 

und auf 4.5.2 bekomme ich:

113.62826122038274 
4637696294982039780 
2.2290860617259246E+49 
5345351685623826105 

Also für exakt den gleichen Eingang, bekomme ich einen anderen Ausgang (wie aus den Bits zu sehen, so dass keine Hin- und Rück Formatierung beteiligt ist)

Weitere Details:

Zusammengestellt einmal VS2015 mit

Beide Maschinen dass ich die Binärdateien leite auf 64bit

Man hat .net 4.5.1 installiert haben, andere die 4.5.2

Just for Klarheit: die String-Konvertierung irrelevant ist ... ich die Änderung der Ergebnisse rega erhalten unabhängig davon, ob es sich um eine String-Konvertierung handelt. Ich habe das nur erwähnt, um die Veränderung zu demonstrieren.

+0

ist net46 auch installiert? – Dbl

+0

@ AndreasMüller - Nein, nur 4.5.2, obwohl die 4.6 die gleiche Antwort wie 4.5.2 manifestiert - die Änderung scheint von 4.5.2 nicht eingeführt zu werden 4.6 –

+0

Übersetzen Sie den Code auf jeder Maschine? Wenn ja, verwendet einer von ihnen Roslyn und der andere den "alten" Compiler? Das könnte Dinge erklären - aber um Dinge zu reduzieren, würde ich vorschlagen, den 'Math.Exp'-Aufruf zu entfernen und einfach' 113.62826122038274.ToString ("R") auszudrucken. Wenn mein Verdacht stimmt, werden Sie auch dort andere Ergebnisse sehen. –

Antwort

7

.NET verwendet die Math-Lib-Funktionen von der CRT, um diese Berechnungen auszuführen. Der von .NET verwendete CRT wird häufig mit jeder Version aktualisiert, so dass Sie erwarten können, dass sich die Ergebnisse zwischen .NET-Versionen ändern, sie werden jedoch immer innerhalb der versprochenen +/1ulp liegen.

+0

Was ist die CRT? – flq

+0

@ flq - C Laufzeit. Die Microsoft C-Standardbibliothek, die mit ihren Compilern ausgeliefert wird. Dieser SO-Post hat viele gute Informationen: http: // stackoverflow.com/questions/2766233/what-is-the-c-Laufzeitbibliothek – antiduh

1

Ich stolperte über das gleiche Problem, aber ich bekomme es nur nach der Installation von. Net 4.6.

Die .Net 4.6-Installation aktualisiert c: \ windows \ system32 \ msvcr120_clr0400.dll und c: \ windows \ syswow64 \ msvc120_clr0400.dll.

die installieren Bevor diese DLLs Datei Eigenschaften-> Details hatte: "Dateiversion 12.0.51689.34249" und "Produktname Microsoft Visual Studio 12 CTP"

Nach .net 4.6 Installation hatten diese „Dateiversion 12.0.52512.0 "und" Produktname Microsoft Visual Studio 2013 "

Ich zwickte meinen Test, um Ihr Beispiel einzuschließen, und ich sehe die gleichen Vorher/Nachher Zahlen wie Sie, indem Sie zwischen Versionen dieser DLL umdrehen.

(Unsere Testsuite all geändertenen Ergebnisse nicht zeigen, wenn wurde auf .net Versionen 4, 4.5, 4.5.1 oder 4.5.2, bis dieser DLLs aktualisiert ausgeführt wird.)

11

Seufz, die Geheimnisse des Schwebens Punkt Mathe weiterhin Programmierer für immer stumpf. Es hat nichts mit der Framework-Version zu tun. Die relevante Einstellung ist Projekt> Eigenschaften> Registerkarte Erstellen.

Platform target = x86: 2.2290860617259248E+49
Platform target = AnyCPU oder x64: 2.2290860617259246E+49

Wenn Sie das Programm auf einem 32-Bit-Betriebssystem laufen dann immer Sie das erste Ergebnis. Beachten Sie, dass das Roundtrip-Format überspezifiziert ist und mehr Ziffern enthält, als ein Double speichern kann. Welches ist 15. Zählen Sie sie ab, erhalten Sie 16. Dies stellt sicher, dass die binäre Darstellung der Doppel, die 1s und 0s die gleichen sind. Der Unterschied zwischen den beiden Werten ist das niedrigstwertige Bit in der Mantisse.

Der Grund dafür, dass das LSB nicht dasselbe ist, liegt darin, dass der x86-Jitter mit dem Generierungscode für the FPU behaftet ist. Welche hat die sehr unerwünschte Eigenschaft der Verwendung mehr Bits der Genauigkeit als ein Doppel kann speichern. 80 statt 64 Bits. Theoretisch um genauere Berechnungsergebnisse zu erhalten. Was es tut, aber rarely in a reproducible way. Kleine Änderungen am Code können zu großen Änderungen im Berechnungsergebnis führen. Wenn Sie den Code mit einem angefügten Debugger ausführen, kann das Ergebnis geändert werden, da dies den Optimierer deaktiviert.

Intel hat diesen Fehler mit dem SSE2-Befehlssatz behoben und die Fließkomma-Anweisungen der FPU vollständig ersetzt. Es verwendet keine extra Genauigkeit, ein Doppel hat immer 64 Bits. Mit der höchst wünschenswerten Eigenschaft, dass das Berechnungsergebnis nicht mehr von der Zwischenspeicherung abhängt, ist es jetzt viel konsistenter. Aber weniger genau.

Dass der x86-Jitter FPU-Anweisungen verwendet, ist ein historischer Unfall. Im Jahr 2002 veröffentlicht, gab es nicht genug Prozessoren, die SSE2 unterstützten. Dieser Unfall kann nicht mehr behoben werden, da er das beobachtbare Verhalten eines Programms verändert. Es war kein Problem für den x64-Jitter, ein 64-Bit-Prozessor wird garantiert auch SSE2 unterstützen.

Ein 32-Bit-Prozess verwendet die Funktion exp(), die FPU-Code verwendet. Ein 64-Bit-Prozess verwendet die Funktion exp(), die SSE-Code verwendet. Das Ergebnis kann um ein LSB unterschiedlich sein. Aber immer noch genau zu 15 signifikanten Ziffern, es ist 2.229086061725925E + 49. Alles, was Sie jemals aus Mathe mit Doppel erwarten können.

+0

Ich bin nicht davon überzeugt, dass dies erklärt "Math.Exp gibt unterschiedliche Ergebnisse nach der Installation von .NET 4.6.1 und Anwendung von KB3098785" https://connect.microsoft .com/VisualStudio/feedback/details/2486915/math-exp-returns-different-result –

+0

Ich habe dir gesagt, was zu tun ist. Versuchen Sie es selbst, ändern Sie das Platform-Ziel und beachten Sie, dass x86 0,71612515940795685 generiert und x64 0,71612515940795696 generiert. Nichts mit der Framework-Version zu tun, alles was mit FPU vs SSE2 zu tun hat. –

+0

In meinem Fall gab jede CPU 2.2290860617259248E + 49, weil Prefer 32bit gesetzt wurde. Aber das gleiche gilt für deine Antwort im Allgemeinen. – cassandrad

1

Um zu zeigen, wie Projekte, die auf verschiedene .NET-Versionen abzielen, die Doppelwandlung von String beeinflussen, habe ich 4 Projekte für verschiedene Versionen erstellt, die alle auf demselben Dev-Rechner mit .NET 4.6 laufen.

Hier ist der Code

double foo = Convert.ToDouble("33.94140881672595"); 

Und hier ist der Ausgang

33,941408816725954 (.NET 4)

33,941408816725946 (.NET 4.5)

33,941408816725946 (.NET 4.5.2)

33.941408816725946 (.NET 4.6)

Also gab es definitiv eine Änderung in der Konvertierungsmethode nach .NET 4