Genauigkeit der Fließkommaarithmetik

8

Ich habe Probleme, die Ausgabe dieses Programms zu verstehen

%Vor%

Die Ausgabe ist

%Vor%

Ich verwende IEEE-Arithmetik. Die Variable y enthält die kleinste mögliche IEEE-Nummer. Die ersten fünf Drucke zeigen eine Zahl, die doppelt so groß ist wie erwartet. Was mich verwirrt ist, dass die nächsten fünf Drucke unterschiedliche Zahlen zeigen. Wenn 1.6*y dasselbe ist wie 2.0*y , wie kann x + 1.6*y von x + 2.0*y abweichen?

    
john 15.03.2013, 19:59
quelle

1 Antwort

8

Auf den Punkt gebracht

Sie sagen, dass Ihr Compiler Visual C ++ 2010 Express ist. Ich habe keinen Zugriff auf diesen Compiler, aber ich verstehe, dass es Programme erzeugt, die anfänglich die x87-CPU so konfigurieren, dass sie 53 Bits Genauigkeit verwendet, um IEEE 754-Berechnungen mit doppelter Genauigkeit so genau wie möglich zu emulieren.

Leider ist "so nah wie möglich" nicht immer nah genug. Historische 80-Bit-Gleitkommaregister können ihre Signifikanz in der Breite begrenzen, um eine doppelte Genauigkeit zu emulieren, behalten jedoch immer einen vollen Bereich für den Exponenten bei. Der Unterschied zeigt sich insbesondere bei der Manipulation von Denormals (wie zB y ).

Was passiert

?

Meine Erklärung wäre, dass in printf("%23.16e\n", 1.6*y); , 1.6*y als eine 80-Bit-Zahl mit reduziertem Signifikanz und vollem Exponenten berechnet wird (es ist also eine normale Zahl), dann in IEEE 754 mit doppelter Genauigkeit umgewandelt (was zu ein Denormal), dann gedruckt.

Andererseits wird in printf("%23.16e\n", x + 1.6*y); , x + 1.6*y mit allen 80-Bit-Zahlen mit reduziertem Signifikanz und vollem Exponenten berechnet (wiederum sind alle Zwischenergebnisse normale Zahlen) und dann in IEEE 754 mit doppelter Genauigkeit umgewandelt gedruckt.

Dies würde erklären, warum 1.6*y dasselbe wie 2.0*y ausgibt, aber einen anderen Effekt, wenn es zu x hinzugefügt wird. Die Zahl, die gedruckt wird, ist eine doppelt genaue Denormalität. Die Zahl, die zu x hinzugefügt wird, ist eine 80-Bit-Zahl mit reduziertem Signifikanz und vollem Exponenten (nicht die gleiche).

Was passiert mit anderen Compilern beim Erzeugen von x87-Anweisungen

?

Andere Compiler, wie GCC, konfigurieren die x87-FPU nicht zur Manipulation von 53-Bit-Signifikanden. Dies kann die gleichen Konsequenzen haben (in diesem Fall würde x + 1.6*y mit allen 80-Bit-Werten mit voller Signifikanz und vollem Exponenten berechnet und dann zum Drucken oder Speichern im Speicher in doppelte Genauigkeit konvertiert). In diesem Fall ist das Problem noch häufiger bemerkbar (Sie müssen keine Denormalen oder unendliche Zahlen verwenden, um Unterschiede zu erkennen).

Dieser Artikel von David Monniaux enthält alle Details, die Sie wünschen, und mehr.

Entfernen des unerwünschten Verhaltens

Um das Problem loszuwerden (wenn Sie es für eins halten), suchen Sie das Flag, das Ihrem Compiler sagt, SSE2-Anweisungen für Fließkommawerte zu generieren. Diese implementieren genau IEEE 754-Semantiken für Einzel- und Doppelpräzision.

    
Pascal Cuoq 15.03.2013, 20:36
quelle