Seltsames Verhalten des Programms in GNU C ++ unter Verwendung von Gleitkommazahlen

9

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.

Bearbeiten:

Vereinfachtes Beispiel für 32-Bit-Compiler:

%Vor%

Ohne Optimierung laufen:

%Vor%

Ausführen mit -ffloat-store:

%Vor%

Lösung:

Wahrscheinlich ist es "kein Fehler" in GCC # 323: Ссылка

Kompilieren mit -ffloat-store löst das Problem.

    
Denis Kirienko 12.11.2014, 11:06
quelle

1 Antwort

4

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 ):

%Vor%

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.

    
LThode 14.11.2014, 22:04
quelle