gcc Präzisionsfehler?

8

Ich kann nur annehmen, dass das ein Fehler ist. Die erste Bestätigung wird ausgeführt, während die zweite fehlschlägt:

%Vor%

Wenn kein Fehler, warum?

    
Jesse Elliott 26.08.2009, 23:03
quelle

7 Antworten

12

Dies ist etwas, das mich auch gebissen hat.

Ja, Fließkommazahlen sollten niemals wegen eines Rundungsfehlers für Gleichheit verglichen werden, und Sie wussten das wahrscheinlich.

Aber in diesem Fall berechnen Sie t1+t2 und berechnen es dann erneut. Sicher das muss ein identisches Ergebnis erzeugen?

Hier ist, was wahrscheinlich vor sich geht. Ich wette, dass Sie das auf einer x86-CPU ausführen, richtig? Die x86-FPU verwendet 80 Bits für ihre internen Register, aber Werte im Speicher werden als 64-Bit-Doppelte gespeichert.

Also t1+t2 wird zuerst mit 80 Bits Genauigkeit berechnet, dann - vermute ich - in sum_2 mit 64 Bits Genauigkeit gespeichert - und es kommt zu einigen Rundungen. Für die Assert wird sie wieder in ein Gleitkommaregister geladen, und t1+t2 wird erneut berechnet, wiederum mit 80 Bits Genauigkeit. Jetzt vergleichen Sie sum_2 , das zuvor auf einen 64-Bit Gleitkommawert gerundet wurde, mit t1+t2 , das mit höherer Genauigkeit (80 Bit) berechnet wurde - und deshalb sind die Werte nicht exakt identisch .

Bearbeiten Warum wird der erste Test bestanden? In diesem Fall wertet der Compiler möglicherweise 4.0+6.3 zur Kompilierzeit aus und speichert sie als 64-Bit-Menge - sowohl für die Zuweisung als auch für die Assertion. Daher werden identische Werte verglichen, und die Bestätigung wird übergeben.

Second Edit Hier ist der Assembler-Code, der für den zweiten Teil des Codes (gcc, x86) mit Kommentaren generiert wurde - folgt ziemlich genau dem oben beschriebenen Szenario:

%Vor%

Interessante Randnotiz: Dies wurde ohne Optimierung kompiliert. Wenn es mit -O3 kompiliert wird, optimiert der Compiler all des Codes entfernt.

    
Martin B 26.08.2009, 23:16
quelle
13

Sie vergleichen Fließkommazahlen. Tun Sie das nicht, Gleitkommazahlen haben unter bestimmten Umständen einen inhärenten Präzisionsfehler. Nimm stattdessen den absoluten Wert der Differenz der beiden Werte und bestätige, dass der Wert kleiner als eine kleine Zahl (Epsilon) ist.

%Vor%

Das hat nichts mit dem Compiler und allem zu tun, was mit der Implementierung von Fließkommazahlen geschieht. Hier ist die IEEE-Spezifikation:

Ссылка

    
Ed S. 26.08.2009 23:06
quelle
3

Ich habe Ihr Problem auf meinem Intel Core 2 Duo dupliziert, und ich habe den Assembly-Code angeschaut. Hier ist, was passiert: Wenn Ihr Compiler t1 + t2 auswertet, tut es

%Vor%

Wenn es in sum_2 speichert, tut es

%Vor%

Dann vergleicht der == -Vergleich die 80-Bit-Summe mit einer 64-Bit-Summe, und sie sind unterschiedlich, hauptsächlich deshalb, weil der Bruchteil-Anteil 0,3 nicht exakt mit einer binären Fließkommazahl dargestellt werden kann eine "Wiederholungs-Dezimalstelle" (tatsächlich binär wiederholend), die auf zwei verschiedene Längen gekürzt wurde.

Was wirklich irritiert ist, dass wenn Sie mit gcc -O1 oder gcc -O2 übersetzen, gcc die falsche Arithmetik zur Kompilierzeit ausführt und das Problem verschwindet. Vielleicht ist das nach dem Standard in Ordnung, aber es ist nur ein weiterer Grund, dass gcc nicht mein Lieblingscompiler ist.

P.S. Wenn ich sage, dass == eine 80-Bit-Summe mit einer 64-Bit-Summe vergleicht, dann meine ich natürlich, dass es die erweiterte Version der 64-Bit-Summe vergleicht. Sie könnten gut daran denken

%Vor%

wird in

aufgelöst %Vor%

und

%Vor%

wird in

aufgelöst %Vor%

Willkommen in der wunderbaren Welt der Fließkomma!

    
Norman Ramsey 26.08.2009 23:46
quelle
3

Wenn Sie Gleitkommazahlen für die Nähe vergleichen, möchten Sie normalerweise ihre relative Differenz messen, die als

definiert ist %Vor%

Zum Beispiel

%Vor%

Die Idee besteht darin, die Anzahl der führenden signifikanten Ziffern zu messen, die die Zahlen gemeinsam haben; Wenn Sie den -log10 von 0.000195787019 nehmen, erhalten Sie 3.70821611, was etwa der Anzahl der führenden 10-stelligen Ziffern entspricht, die alle Beispiele gemeinsam haben.

Wenn Sie feststellen müssen, ob zwei Gleitkommazahlen gleich sind, sollten Sie etwas wie

tun %Vor%

wobei die Maschine epsilon die kleinste Zahl ist, die in der Mantisse der verwendeten Gleitkomma-Hardware gehalten werden kann. Die meisten Computersprachen haben einen Funktionsaufruf, um diesen Wert zu erhalten. error_factor sollte auf der Anzahl der signifikanten Ziffern basieren, von denen Sie glauben, dass sie durch Rundungsfehler (und andere) in den Berechnungen der Zahlen x und y verbraucht werden. Wenn ich zum Beispiel wüsste, dass x und y das Ergebnis von ungefähr 1000 Summierungen sind und keine Grenzen bei den summierten Zahlen kennen, würde ich error_factor auf ungefähr 100 setzen.

Versucht, diese als Links hinzuzufügen, konnte dies aber nicht, da dies mein erster Beitrag ist:

  • en.wikipedia.org/wiki/Relative_difference
  • en.wikipedia.org/wiki/Machine_epsilon
  • en.wikipedia.org/wiki/Significand (Mantisse)
  • de.wikipedia.org/wiki/Rounding_error
Jeff Kubina 27.08.2009 02:09
quelle
2

Es kann sein, dass Sie in einem der Fälle ein 64-Bit-Double mit einem internen 80-Bit-Register vergleichen. Es mag aufschlussreich sein, die Montageanweisungen zu lesen, die GCC für die beiden Fälle ausgibt ...

    
Jim Lewis 26.08.2009 23:12
quelle
1

Vergleiche von Zahlen mit doppelter Genauigkeit sind von Natur aus ungenau. Zum Beispiel können Sie oft 0.0 == 0.0 finden, die false zurückgibt. Dies liegt an der Art und Weise, wie die FPU Nummern speichert und verfolgt.

Wikipedia sagt :

  

Das Testen auf Gleichheit ist problematisch. Zwei mathematisch identische Rechenfolgen können durchaus unterschiedliche Fließkommawerte erzeugen.

Sie müssen ein Delta verwenden, um eine Toleranz für Ihre Vergleiche anzugeben, anstatt einen genauen Wert.

    
Matthew Iselin 26.08.2009 23:08
quelle
0

Dieses "Problem" kann mit diesen Optionen behoben werden:

-msse2 -mfpmath = sse

wie auf dieser Seite erklärt:

Ссылка

Sobald ich diese Optionen verwendet habe, bestanden beide.

    
Jesse Elliott 27.08.2009 21:52
quelle

Tags und Links