Ich weiß, dass die ==
Überprüfung der Gleichheit von Gleitkommavariablen kein guter Weg ist. Aber ich möchte das nur mit den folgenden Aussagen wissen:
float x = ...
float y = x;
assert(y == x)
Wird die Behauptung wahr sein, da sie von y
kopiert x
wurde?
c++
floating-point
Wei Li
quelle
quelle
-m32
) oder durch Anweisung von GCC zur Verwendung der x87-FPU (-mfpmath=387
) durchführen.Antworten:
Neben dem
assert(NaN==NaN);
von kmdreko aufgezeigten Fall können mit x87-math Situationen auftreten, in denen 80-Bit-Floats vorübergehend im Speicher gespeichert und später mit Werten verglichen werden, die noch in einem Register gespeichert sind.Mögliches minimales Beispiel, das beim Kompilieren mit gcc9.2 fehlschlägt mit
-O2 -m32
:Godbolt Demo: https://godbolt.org/z/X-Xt4R
Das
volatile
kann wahrscheinlich weggelassen werden, wenn Sie es schaffen, einen ausreichenden Registerdruck zu erzeugen, um ihn zuy
speichern und aus dem Speicher neu zu laden (aber den Compiler genug zu verwirren, um den Vergleich nicht insgesamt wegzulassen).Siehe GCC FAQ Referenz:
quelle
float
mit Standardgenauigkeit mit zusätzlicher Genauigkeit berücksichtigt werden .-ffloat-store
scheint dies der Weg zu sein, um dies zu verhindern.Es wird nicht wahr sein , wenn
x
istNaN
, da VergleicheNaN
sind immer falsch (ja, auchNaN == NaN
). Für alle anderen Fälle (Normalwerte, Subnormalwerte, Unendlichkeiten, Nullen) ist diese Behauptung wahr.Der Rat zur Vermeidung von Gleitkommazahlen
==
gilt für Berechnungen, da Gleitkommazahlen nicht viele Ergebnisse genau ausdrücken können, wenn sie in arithmetischen Ausdrücken verwendet werden. Die Zuweisung ist keine Berechnung und es gibt keinen Grund, warum die Zuweisung einen anderen Wert als das Original ergeben würde.Eine Bewertung mit erweiterter Genauigkeit sollte kein Problem darstellen, wenn der Standard eingehalten wird. Von
<cfloat>
von C geerbt [5.2.4.2.2.8] ( Schwerpunkt Mine ):Da jedoch die Kommentare haben darauf hingewiesen, einige Fälle mit bestimmten Compilern, Build-Optionen und Ziele könnten machen dies paradoxerweise falsch.
quelle
x
in einem Register in der ersten Zeile berechnet wird , für eine höhere Präzision als das Minimum zu haltenfloat
. Dasy = x
kann im Speicher sein und nurfloat
Präzision behalten . Dann würde der Test auf Gleichheit mit dem Speicher gegen das Register mit unterschiedlichen Präzisionen und somit ohne Garantie durchgeführt.x+pow(b,2)==x+pow(a,3)
kann davon abweichen,auto one=x+pow(b,2); auto two=y+pow(a,3); one==two
weil einer mit größerer Genauigkeit als der andere vergleichen kann (wenn ein / zwei 64-Bit-Werte in RAM sind, während Zwischenwerte 80-Bit-Werte in fpu sind). Ein Auftrag kann also manchmal etwas bewirken.gcc -ffloat-store
für strikte Einhaltung erzwingen . Bei dieser Frage geht es jedoch darum,x=y; x==y;
dazwischen nichts zu tun. Wenny
es bereits gerundet ist, um in einen Float zu passen, ändert die Konvertierung in Double oder Long Double und Back den Wert nicht. ...Ja,
y
wird sicher den Wert annehmen vonx
:Es gibt keinen Spielraum für die Zuweisung anderer Werte.
(Andere haben bereits darauf hingewiesen, dass ein Äquivalenzvergleich
==
dennoch zu bewerten istfalse
NaN-Werte ergibt.)Das übliche Problem mit Gleitkomma
==
ist, dass es leicht ist, nicht ganz den Wert zu haben, den Sie zu tun glauben. Hier wissen wir, dass die beiden Werte, was auch immer sie sind, gleich sind.quelle
[expr]
. Wenn ich die Links ignorieren und mich auf die Zitate konzentrieren möchte, bleibt mir die Verwirrung, dass z. B. C.5.3 die Verwendung des Begriffs "Wert" oder des Begriffs "Ergebnis" nicht zu behandeln scheint (obwohl dies der Fall ist) benutze "result" einmal in seinem normalen englischen Kontext). Vielleicht könnten Sie klarer beschreiben, wo der Standard Ihrer Meinung nach unterscheidet, und ein einziges klares Zitat zu diesem Ereignis liefern. Vielen Dank!Ja, in allen Fällen (ohne Berücksichtigung von NaNs und x87-Problemen) ist dies der Fall.
Wenn Sie eine
memcmp
Prüfung durchführen, können Sie die Gleichheit testen und NaNs und sNaNs vergleichen. Dazu muss der Compiler auch die Adresse der Variablen übernehmen, wodurch der Wert in einen 32-Bit-Wertfloat
anstelle eines 80-Bit- Werts umgewandelt wird . Dadurch werden die x87-Probleme behoben. Die zweite Behauptung hier soll nicht zeigen, dass==
NaNs nicht als wahr verglichen werden:Beachten Sie, dass wenn die NaNs eine andere interne Darstellung haben (dh eine unterschiedliche Mantisse), die
memcmp
nicht wahr ist.quelle
In normalen Fällen würde es als wahr ausgewertet. (oder die assert-Anweisung macht nichts)
Bearbeiten :
Mit "normalen Fällen" meine ich, dass die oben genannten Szenarien (wie NaN-Werte und 80x87-Gleitkommaeinheiten), wie sie von anderen Benutzern angegeben wurden, ausgeschlossen werden.
Angesichts der Veralterung von 8087-Chips im heutigen Kontext ist das Problem eher isoliert und die Frage, ob sie im aktuellen Zustand der verwendeten Gleitkomma-Architektur anwendbar ist, gilt für alle Fälle mit Ausnahme von NaNs.
(Referenz zu 8087 - https://home.deec.uc.pt/~jlobo/tc/artofasm/ch14/ch143.htm )
Ein großes Lob an @chtz für die Wiedergabe eines guten Beispiels und an @kmdreko für die Erwähnung von NaNs - wusste vorher nichts davon!
quelle
x
, sich in einem Gleitkommaregister zu befinden, währendy
es aus dem Speicher geladen wird. Der Speicher ist möglicherweise weniger genau als ein Register, sodass der Vergleich fehlschlägt.float
Wert ohne zusätzliche Genauigkeit handelt.int a=1; int b=a; assert( a==b );
eine Behauptung ausgelöst wird , halte ich es nur für sinnvoll, diese Frage in Bezug auf einen korrekt funktionierenden Compiler zu beantworten (wobei möglicherweise festgestellt wird, dass einige Versionen einiger Compiler dies tun / haben) -wurde-bekannt-das falsch zu verstehen). In der Praxis, wenn aus irgendeinem Grunde ein Compiler entfernt nicht die zusätzliche Präzision aus dem Ergebnis einer Register gespeicherten Zuordnung, sollte es so tun , bevor es verwendet diesen Wert.Ja, es wird immer True zurückgegeben , außer wenn es NaN ist . Wenn der Variablenwert NaN ist, wird immer False zurückgegeben !
quelle