Verstehe float-Vergleich nicht !
-
Hi zusammen,
ich hab hier im Forum einen Link zu ner Seite entdeckt, auf der über das Vergleichen von float Variablen diskutiert wird. Dabei war z.B eine Funktion, mit der man einen float innerhal einer Toleranz auf 0 überprüfen kann. Ich versteh sie aber leider nicht und genau das macht es für mich auch unmöglich sie auf äquivalent auf double zu übertragen. Hier die Funktionbool CompareZero(float f, float tolerance) { int fi = *reinterpret_cast<int*>(&f); int ti = *reinterpret_cast<int*>(&tolerance); return (fi & 0x7fffffff) <= ti; }Ich hoff mal jemand kann mir das erklären.
MfG CatDog
-
Hallo,
ich versuch mich mal daran:
1. zeile f wird zu int gecastet ==> alles hinter dem Dezimalpunkt fällt weg
2. zeile tolerance wird auf int gecastet ==> alles hinter dem Dezimalpunkt fällt weg3. zeile:
0x7fffffff ist im Hexadezimalsystem: 2147483647( maximalwert eines Int)
& ist eine Bitoperation ==> wenn alle Operanden eine 1 haben ist es 1
( fi & 0x7fffffff ) ist sowie ich das verstehe das gleiche wie fi
und
<= ti ist halt der Toleranzvergleich, wenns kleiner gleich ti ist kommt true.
Korrigiert mich, wenn ich mich irre, bin selbst neuling

-
ich weiß ja nicht, ob das so gut ist, einen float* nach int* zu casten...
Hab sowas verrücktes noch nie probiert
Aus meiner Sicht macht das wenig Sinn...
-
Zunächst einmal ist das Ganze undefiniert, wegen unzulässigem Aliasing. Schreiben wir das Ganze also erst einmal um:
bool CompareZero(float f, float tolerance) { unsigned fi, ti; // static_assert< sizeof fi == sizeof f >; memcpy( &fi, &f, sizeof fi ); memcpy( &ti, &tolerance, sizeof ti ); return ( fi & 0x7fffffff) <= ti; }Neben der Bedingung gleicher Größe müssen noch drei weitere Bedingungen erfüllt sein:
1. unsigned hat keine Trapdarstellungen für zulässige float-Werte
2. Die Endianess ist für beide Typen gleich (trifft auf ein paar exotische Systeme nicht zu)
3. Die interne Darstellung von floats ist derart, dass die kleiner-als Relation ohne Beachtung des Vorzeichens korrekt abgebildet wird (trifft. u.a. für alle IEEE 754-konformen Systeme zu).
Letztere Bedingung sorgt dafür, dass der Code funktioniert.
Die und-Verknüpfung dient nur dazu, das evtl. vorhandene Vorzeichen von f loszuwerden.Eine Version für double könnte so aussehen:
bool CompareZero(double f, double tolerance) { unsigned long long fi, ti; memcpy( &fi, &f, sizeof fi ); memcpy( &ti, &tolerance, sizeof ti ); return ( fi & ( ~0ull >> 1 ) ) <= ti; }
-
Die casts sind da um die Bitmuster der beiden floats als int behandenln zu können. Der Code geht ab da nun davon aus dass es sich um IEEE 754 floats (oder was ähnliches) handelt. Die UND Verknüpfung mit 0x7FFFFFFF killt das Vorzeichenbit, macht also praktisch abs(). Der Vergleich nacher funktioniert weil aufgrund des Aufbaus von floats Relationen gleich bleiben auch wenn die Bitmuster als int behandelt werden. Der Code ist also funktional äquivalent zu
return abs(f) <= tolerance;Warum genau willst du so einen Hack verwenden?
-
camper schrieb:
Zunächst einmal ist das Ganze undefiniert [...]
Das isses doch so oder so!?
-
Weil in dem Beitrag steht, dass es meistens viel schneller ist. Ich hab mir das hauptsächlich angeschaut, da man ja manchmal noch irgendwo 1-2% Geschw. sucht, aber hauptsächlich hab ichs einfach nicht kapiert, und sowas wurmt mich.
Was das 0x7fffffff genau ist, hab ich immer noch nicht kapiert. Gibt es noch mehr signifikante Hex-Zeichen, die man wissen sollte ?
Gruss
-
CatDog11 schrieb:
Weil in dem Beitrag steht, dass es meistens viel schneller ist.
Ich würd solche Mikrooptimierungen lieber dem Compiler überlassen bis ich wirklich ein Performanceproblem hab und gemessen hab dass tatsächlich diese float Vergleichen daran schuld sind. Um ehrlich zu sein würds mich nicht wundern wenn der Compiler sogar besser optimieren würde als dieser Hack...
CatDog11 schrieb:
Was das 0x7fffffff genau ist, hab ich immer noch nicht kapiert.
0x7FFFFFFF entspricht binär 01111111 11111111 11111111 11111111.
-
camper schrieb:
Eine Version für double könnte so aussehen:
Eine Version für double könnte so aussehen:
-
Wutz schrieb:
camper schrieb:
Eine Version für double könnte so aussehen:
Eine Version für double könnte so aussehen:
1. vergleicht das auf exakt 0 und nicht auf |x| < epsilon und
2. versteh ich nicht warum man da nicht einfach x == 0.0 machen sollte
-
dot schrieb:
camper schrieb:
Zunächst einmal ist das Ganze undefiniert [...]
Das isses doch so oder so!?
Warum?
-
camper schrieb:
dot schrieb:
camper schrieb:
Zunächst einmal ist das Ganze undefiniert [...]
Das isses doch so oder so!?
Warum?
Na weil der Standard überhaupt keine Aussagen über das Bitmuster eines float macht...
Aber gut was das Aliasing angeht hast du natürlich recht.
-
dot schrieb:
Na weil der Standard überhaupt keine Aussagen über das Bitmuster eines float macht...
13.9.1/8 schrieb:
The value representation of floating-point types is implementation-defined.
ist ein bisschen mehr als keine Aussage.
-
dot schrieb:
Wutz schrieb:
camper schrieb:
Eine Version für double könnte so aussehen:
Eine Version für double könnte so aussehen:
1. vergleicht das auf exakt 0 und nicht auf |x| < epsilon und
2. versteh ich nicht warum man da nicht einfach x == 0.0 machen solltex == 0.0 ist schlecht, weil es z.B. auch -0.0 im floating point format gibt

allen, die sicht mit floating point rumaergern muessen, kann ich nur Empfehlen mal einen Blick auf diesen Aufsatz zu werfen:
http://download.oracle.com/docs/cd/E19957-01/806-3568/ncg_goldberg.html
gruss
Dirk
-
dbu schrieb:
x == 0.0 ist schlecht, weil es z.B. auch -0.0 im floating point format gibt

Das wäre ein weiteres Problem im von Wutz verlinkten Code. Der normale Vergleich mit == sollte damit allerdings kein Problem haben (wenn wir davon ausgehen dass es sich um IEEE 754 floats handelt).
dbu schrieb:
http://download.oracle.com/docs/cd/E19957-01/806-3568/ncg_goldberg.html
Ja der Artikel ist wirklich jedem sehr zu empfehlen.
-
CatDog11 schrieb:
bool CompareZero(float f, float tolerance) { int fi = *reinterpret_cast<int*>(&f); int ti = *reinterpret_cast<int*>(&tolerance); return (fi & 0x7fffffff) <= ti; }Wenn du schon eine eigene Funktion dafür einsetzen möchtest, dann bitte nicht mit casts sondern schlicht und einfach mit einer Abfrage if fabs(f < tolerance) oder dasselbe mit fabsl.