Schau dir dieses Programm an:
%Vor%Funktion dist gibt die Entfernung zwischen zwei Punkten zurück. A, B, C, D sind Ecken des Quadrats.
Es sollte dist (A, B) == dist (B, C) == dist (C, D) == dist (D, A) == sqrt (2) sein.
Und dist (A, B) + dist (C, D) == dist (A, D) + dist (B, C) == 2 * sqrt (2)
Ich verwende GNU / Linux, i586, GCC 4.8.2.
Ich kompiliere dieses Programm und führe:
%Vor%Wir sehen, dass das Programm die gleichen Werte von dist (A, B) + dist (C, D) und dist (A, D) + dist (B, C) ausgibt, aber die Bedingung dist (A, B) + dist (C, D) & gt; dist (A, D) + dist (B, C) ist wahr!
Wenn ich mit -O2 kompiliere, sieht es OK aus:
%Vor%Ich denke, es ist ein gcc-Fehler, und er steht nicht in direktem Zusammenhang mit der Genauigkeit der Gleitkommaoperation, weil in diesem Fall die Werte dist (A, B) + dist (C, D) und dist (A, D ) + dist (B, C) MUSS gleich sein (jeder von ihnen ist sqrt (2) + sqrt (2)).
Wenn ich die Funktion dist:
ändere %Vor%Das Programm läuft korrekt. Also das Problem nicht in Gleitkommadarstellung von Zahlen, sondern im gcc-Code.
Vereinfachtes Beispiel für 32-Bit-Compiler:
%Vor%Ohne Optimierung laufen:
%Vor%Ausführen mit -ffloat-store:
%Vor%Wahrscheinlich ist es "kein Fehler" in GCC # 323: Ссылка
Kompilieren mit -ffloat-store löst das Problem.
Dieses scheinbar seltsame Verhalten ist auf die Funktionsweise der alten x87-Gleitkommaeinheit zurückzuführen: Sie verwendet einen 80-Bit long double
-Typ mit 64 Bits Genauigkeit als Registerformat, während temporäre double
s 64 Bits lang sind mit 53 Bits Präzision. Was passiert, ist, dass der Compiler 1 der sqrt(2)
Ergebnisse in den Speicher übergibt (da sqrt
ein double
zurückgibt, das auf den 53-bit Signifikanten dieses Typs aufrundet), so dass der FP-Registerstapel für den Nächster Aufruf von sqrt(2)
. Dann vergleicht er den 53-Bit-Wert des Arbeitsspeichers mit dem nicht gerundeten 64-Bits-Wert, der vom anderen sqrt(2)
-Aufruf zurückkommt, und sie kommen anders, weil sie anders gerundet sind, als Sie können sehen, von dieser Assembler-Ausgabe (Anmerkungen meins, verwendet Ihr zweites Code-Snippet mit der 2s zur Klarheit auf 2.0s und -Wall -O0 -m32 -mfpmath=387 -march=i586 -fno-builtin
für Compile-Flags auf Godbolt geändert ):
Urteil: Der x87 ist der Spinner. -mfpmath=sse
ist die definitive Korrektur für dieses Verhalten - es wird GCC FLT_EVAL_METHOD
auf 0 definieren, da die SSE (2) Gleitkommaunterstützung nur Single / Double ist. Der Schalter -ffloat-store
funktioniert auch für dieses Programm, wird jedoch nicht als allgemeine Problemumgehung empfohlen - er wird Ihr Programm aufgrund der zusätzlichen Überfüllungen / Füllungen langsamer machen und funktioniert nicht in allen Fällen. Natürlich wird dies auch durch eine 64-Bit-CPU / OS / Compiler-Kombination behoben, da die x86-64 ABI standardmäßig SSE2 für Fließkomma-Berechnungen verwendet.
Tags und Links c++ gcc compiler-optimization floating-point double