float-Vergleich: Ist mein Compiler "kaputt"?



  • Hi,

    wenn ich zwei float-Zahlen-Vergleiche (f ist also vom Typ float!!!):

    if (f == 0.0f)
    ...
    

    Ergibt das doch true, wenn - Epsilon < f < + Epsilon, oder etwa nicht? So steht es jedenfalls in float.h.

    Bei diesem Vergleich ergibt sich bei mir jedoch false für ein f mit dem Wert
    -5.06621e-011 (VC++ 6.0).

    Wenn ich vorher aber "f = 0.0f;" setze, ergibt f == 0.0f korrekterweise true.

    if (f == 1.0f)
    ...
    

    ergibt false für ein f, was 1.0f ist wenn ichs mit fout ausgebe, mit printf("%.23e") ergibt sich 1.0000001192092896000000000e+000, was komischerweise genau 1.0f + Epsilon ist, ab dieser Zahl ist ja 1.0f != f, somit ist das Ergebnis hier zu verstehen.

    Aber es ist kaum zu glauben, dass sich bei der Berechnung vorher zufälligerweise exakt genau 1.0f + Epsilon ergibt, so dass der Ausdruck ungleich ist.
    Ist das ein schlechter Aprilscherz oder woran könnte das liegen? Hab vorher ganz normal das Punktprodukt zweier Vektoren berechnet, die parallel sind, und es ergab sich diese Zahl.

    Ich hab keinen Bock in jedem float-Vergleich eine Zahl abzuziehen, ne korrekte library zu verwenden oder double zu nehmen (zu groß und zu langsam).

    Gibt es ne Möglichkeit, diese Ungenauigkeiten zu unterbinden????



  • JensE schrieb:

    wenn ich zwei float-Zahlen-Vergleiche (f ist also vom Typ float!!!):

    if (f == 0.0f)
    ...
    

    Ergibt das doch true, wenn - Epsilon < f < + Epsilon, oder etwa nicht? So steht es jedenfalls in float.h.

    Gleichheit heißt Gleichheit, nicht "ungefähr gleich".

    Bei diesem Vergleich ergibt sich bei mir jedoch false für ein f mit dem Wert
    -5.06621e-011 (VC++ 6.0).

    Offensichtlich ungleich 0.

    Wenn ich vorher aber "f = 0.0f;" setze, ergibt f == 0.0f korrekterweise true.

    Nicht verwunderlich.

    Aber es ist kaum zu glauben, dass sich bei der Berechnung vorher zufälligerweise exakt genau 1.0f + Epsilon ergibt, so dass der Ausdruck ungleich ist.

    Warum nicht?

    Ich hab keinen Bock in jedem float-Vergleich eine Zahl abzuziehen, ne korrekte library zu verwenden oder double zu nehmen (zu groß und zu langsam).

    Wirst du wohl müssen. BTW ist double zwar groß, aber abgesehen davon nicht langsam.



  • floats und doubles kann man nicht mit == vergleichen, weil es immer wieder Rundungsfehlern auftretten. Die beste Möglichkeit ist:

    #include <math.h>
    
    #define EPSILON 0.001
    
    int fequals(float a, float b)
    {
        return fabsf(a-b) < EPSILON
    }
    


  • Ok, mag sein, aber was bedeutet das (in float.h):

    #define FLT_EPSILON     1.192092896e-07F        /* smallest such that 1.0+FLT_EPSILON != 1.0 */
    

    Damit ist meiner Ansicht nach gemeint, dass der Vergleich

    (f != 1.0f)
    

    in einem Ansi C-Compiler genau dann true ergibt, wenn f größer als 1.0f + FLT_EPSILON ist.

    Das kann man doch auf Vergleiche mit beliebigen Zahlen übertragen, also auch auf Vergleiche mit 0.

    Und wenn f und g zwei float-Variablen sind, ergibt dann in einem C-Compiler
    "(f == g)" nur dann true, wenn alle 38-float-Nachommastellen gleich sind?

    Kann ich mir kaum vorstellen!



  • JensE schrieb:

    Ok, mag sein, aber was bedeutet das (in float.h):

    #define FLT_EPSILON     1.192092896e-07F        /* smallest such that 1.0+FLT_EPSILON != 1.0 */
    

    Damit ist meiner Ansicht nach gemeint, dass der Vergleich

    (f != 1.0f)
    

    in einem Ansi C-Compiler genau dann true ergibt, wenn f größer als 1.0f + FLT_EPSILON ist.

    größer gleich, aber ansonsten korrekt. Das kannst du aber nicht auf die Situation mit 0.0f übertragen. Es heißt ja nicht umsonst "Fließ"-Kommazahl. Die Genauigkeitsangabe bei float bezieht sich auf die Anzahl signifikanter Stellen (nicht der Nachkommastellen). float hat etwa 7 signifikante Stellen ("etwa", weil das intern natürlich binär ist, und sich 23 Bits nicht 1:1 in Dezimalstellen umrechnen lassen). Wenn du also z.B. 1.000001 hast, sind die Stelle ausgereizt, genauer gehts nicht (etwas vereinfacht). Bei 0.000001 sieht das anders aus, weil die 1 die erste signifikante Stelle ist, danach kommen noch 6 weitere.

    Und wenn f und g zwei float-Variablen sind, ergibt dann in einem C-Compiler
    "(f == g)" nur dann true, wenn alle 38-float-Nachommastellen gleich sind?

    Jein. Der Vergleich ist genau dann true, wenn alle signifikanten Stellen gleich sind. Und das sind, wie gesagt, nur 7.

    Hier noch Lesestoff:
    http://www.psc.edu/general/software/packages/ieee/ieee.html





  • Ok,

    vielen Dank für die Antworten und Links!!!!!!!


Anmelden zum Antworten