Vergleich von float
-
Vielleicht kann mir ja jemand folgendes merkwürdiges Verhalten erklären.
Ich vergleiche zwei float-Werte mit ==. In meinen Fall sollte das in Ordnung sein, da das im Rahmen einer Test-Funktion passiert, die überprüft, ob ein Wert richtig gesetzt wurde.
Die fragliche Zeile ist folgende:
if (pc[i][j] != p[j]) {Der if-Block wird betreten. Wenn ich beide Werte (pc[i][j] und pc[j]) ausgebe, werden exakt gleiche Zahlen angezeigt (auch mit setprecision(20)).
Jetzt wir es aber noch kurioser. Ich habe vor die Zeile mit "if" eine Ausgabe "cout << i << endl;" eingefügt. Plötzlich wird der if-Block nicht mehr betreten.
Gelöst hab ich das Problem, indem ich die Bedingnung zu
if (reinterpret_cast<const int&>(pc[i][j]) != reinterpret_cast<const int&>(p[j])) {geändert hab.
Ist das ganze eine sehr merkwürdige Optimierung des GCC (der Bug trat nur im Release-Modus auf) oder wie muss ich mir das jetzt erklären?
-
Warum denn kein Epsilonvergleich? Du wirst so nicht glücklich werden.
-
Das ist keine "merkwürdige Optimierung des gcc", das ist dein Unsinnscast, der Probleme macht.
Erkläre doch mal, was dein Cast bringen soll.
-
Warum ein Epsilonvergleich, wenn man weiß, dass die Werte EXAKT gleich sind. Exakt heißt hier, dass die Bitvektoren der beiden Werte übereinstimmen (deshalb funktioniert ja der reinterpret-cast).
Epsilonvergleich sagt sich immer so leicht. Ein einfaches ist für ein konstantes aber murks, da man die Größenordnung von a und b nicht kennt. Also müsste von a und b abhängen, z.B. \epsion = (a+b)/10^9. Warum sollte hier aber ein guter Wert sein? Vermutlich kann man ganze Paper darüber schreiben, wie man Fließkommazahlen vergleicht.
Wutz hat mich scheinbar falsch verstanden. Ich stimme zu, dass der Cast natürlich keine schöne Lösung ist, aber er liefert im Gegensatz zum direkten float-Vergleich korrekte Ergebnisse.
Was der Cast bringen soll? Naja, er verhindert, dass der gcc merkwürdige Optimierungen durchführt.
Um nochmal die Situation zu verdeutlichen.
Ich habe sowas wie:std::vector<float> v; float x = 1.7; v.push_back(x); if (v[0] != x) {der if-Block dürfte meiner Meinung nach NIE betreten werden. In meinem Fall wurde der if-Block aber (in einer ähnlichen Situation) betreten (und zwar nur im Release-Modus).
-
Vermutlich kann man ganze Paper darüber schreiben, wie man Fließkommazahlen vergleicht.
Ja, das gibt es auch: http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
Das ist keine "merkwürdige Optimierung des gcc", das ist dein Unsinnscast, der Probleme macht.
Den Cast hat er eingefügt, nachdem der Bug auftrat.
-
Arcoth schrieb:
Den Cast hat er eingefügt, nachdem der Bug auftrat.
Ja, aber der ist doch trotzdem Quatsch. Wenn die beiden Floats ein identisches Bitpattern haben, wird auch ein normaler Floatvergleich true ergeben. Das Problem tritt doch dann auf, wenn sie nicht das gleiche Bitpattern haben, obwohl die Operanden gleich wären, hätte man die Rechnung mit reellen Zahlen durchgeführt.
Lesen:
http://www.cs.ucdavis.edu/~bai/ECS231/Goldberg91.pdf (PDF)
http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html (HTML)@Ramanujan: deine Erklärung für die Beobachtung passt nicht. Entweder hast du was übersehen oder die Ursache liegt ganz woanders. Zeig mal bitte die Zuweisung und Deklarationen der beteiligten Variablen. Möglichst aus dem echten Code, mit dem der Fehler auftritt.
-
Und ja, du hast völlig Recht, dass in der geschilderten Situation ein Epsilonvergleich o.ä. völlig unnötig ist.
-
Achso: was heißt denn bitte Releasemodus in Bezug auf GCC? Der kennt sowas nicht.
Genauer Compileraufruf wäre hilfreich.Bitte entschuldigt die mehrfachen Beiträge. Nächstes Mal überlege ich vor dem ersten Absenden, ob ich nicht noch was anderes schreiben will.
-
Ja, aber der ist doch trotzdem Quatsch. Wenn die beiden Floats ein identisches Bitpattern haben, wird auch ein normaler Floatvergleich true ergeben.
Das ist mir klar.
Deine Links sind übrigens völlig outdated (oder ist das hier egal?).
-
Arcoth schrieb:
Deine Links sind übrigens völlig outdated (oder ist das hier egal?).
Die sind von 1991. Seitdem hat sich bei den Grundlagen der Gleitkommaarithmetik nichts getan. Selbst wenn es noch älter wäre (aus einer Zeit vor IEEE-754 z.B.), wäre das Paper noch lesenswert. Ob man jetzt die eine oder andere Implementierung zur Illustration verwendet, ist nicht so wichtig. IEEE-Floats sind halt praxisnäher.
Genau die erklärt der Artikel aber eben schon und ist damit trotz seines Alters topaktuell.
-
Releasemodus heißt in meinem Fall "-O2".
Der Compileraufruf ist:
g++ -c -pipe -fno-keep-inline-dllexport -O2 -std=c++0x -frtti -Wall -Wextra -fexceptions -mthreads -DUNICODE -I../RangeClustering -I'.' -I'../../5.2.1/mingw48_32/mkspecs/win32-g++' -o release/Test.o ../RangeClustering/Test.cppIch hab meinen Code mal als zip-Archiv hochgeladen:
http://depot.tu-dortmund.de/get/6e6ruDie Bedingung steht in der test.cpp in Zeile 107.
Wenn man nicht zu kurzen Code vor der If-Bedingung einfügt (z.B. ein "cout << i << endl;" oder auch die reinterpret-cast-Version der If-Bedingung), tritt der Bug nicht mehr auf.