Herzlich Willkommen, lieber Gast!
  Sie befinden sich hier:

  Forum » C / C++ (ANSI-Standard) » Vergleich von Flieskommazahlen

Forum | Hilfe | Team | Links | Impressum | > Suche < | Mitglieder | Registrieren | Einloggen
  Quicklinks: MSDN-Online || STL || clib Reference Grundlagen || Literatur || E-Books || Zubehör || > F.A.Q. < || Downloads   

Autor Thread - Seiten: > 1 < [ 2 ]
000
07.07.2006, 14:57 Uhr
(un)wissender
Niveauwart


Man sagt ja, dass man Flieskommazahlen nicht mit == vergleichen soll.
Nun fällt es mir mit dem gcc aber schwer ein Beispiel zu finden, wo der Vergleich mit == wirklich ein falsches Ergebnis liefert. Kennt jemand eins?

Ansonsten habe ich mal das hier geschrieben, dass den Vergleich von Flieskommazahlen ermöglichen soll.



C++:
#include <limits>
#include <cmath>

namespace fl
{
    template<typename T>
    bool eq(T first, T second,
        T epsilon = std::numeric_limits<T>::epsilon())
    {
        return std::abs(first - second) < epsilon;
    }  
    
    template<typename T>
    bool neq(T first, T second,
        T epsilon = std::numeric_limits<T>::epsilon())
    {
        return !eq(first, second, epsilon);
    }
    
    template<typename T>
    bool le(T first, T second,
        T epsilon = std::numeric_limits<T>::epsilon())
    {
        return neq(first, second, epsilon) && first < second;
    }

    template<typename T>        
    bool gr (T first, T second,
        T epsilon = std::numeric_limits<T>::epsilon())
    {
        return neq(first, second, epsilon) && first > second;
    }    

    template<typename T>    
    bool eqLe (T first, T second,
        T epsilon = std::numeric_limits<T>::epsilon())  
    {
        return eq(first, second, epsilon) || first < second;
    }    

    template<typename T>    
    bool eqGr(T first, T second,
        T epsilon = std::numeric_limits<T>::epsilon())
    {
        return eq(first, second, epsilon) || first > second;
    }    
}


--
Wer früher stirbt ist länger tot.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
001
07.07.2006, 15:23 Uhr
ao

(Operator)



Zitat von (un)wissender:
Nun fällt es mir mit dem gcc aber schwer ein Beispiel zu finden, wo der Vergleich mit == wirklich ein falsches Ergebnis liefert. Kennt jemand eins?

Hier ist eins, das mit VC6 danebengeht:

C++:
#include <iostream>
#include <cmath>

double test ()
{
    return (sqrt (3) * sqrt(3) - 3); // wurzel 3 * wurzel 3 - 3 sollte 0 sein
}

int main (void)
{
    std::cout << test ();
    return 0;
}


Ergebnis:

Code:
-4.44089e-016

 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
002
07.07.2006, 16:12 Uhr
(un)wissender
Niveauwart


Hm, ja.
Wobei hier auch der Epsilonvergleich nicht hilft, da kummulierter Fehler. Oder man muss Epsilon größer wählen.
--
Wer früher stirbt ist länger tot.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
003
07.07.2006, 16:57 Uhr
ao

(Operator)


Das musst du sowieso. Mit kumulierten Fehlern hast du es in der Praxis immer zu tun.

Ich würde das Vergleichs-Epsilon zwei bis drei Zehnerpotenzen größer als numeric_limits<T>::epsilon () wählen, weil die letzten zwei bis drei Stellen eigentlich immer Rauschen sind.

Zu deinen Vergleichs-Templates:

Es gibt ein Entwurfsmuster, nachdem man von den sechs möglichen Vergleichsoperatoren nur zwei ausformulieren muss; die anderen ergeben sich nur durch einfache logische Verknüpfungen dieser beiden.

Vorteil: Die eigentliche Entscheidungslogik (das, was verifiziert werden muss) liegt in ganz wenigen Codezeilen in nur zwei Funktionen. Den anderen vieren sieht man auf einen Blick an, dass sie korrekt sind (vorausgesetzt, die zwei ersten sind richtig).


C++:
bool eq   /* == */ (T a, T b) { return _Equal (a, b); }
bool lt   /* <  */ (T a, T b) { return _LessThan (a, b); }

bool neq  /* != */ (T a, T b) { return !eq (a, b); }
bool gteq /* >= */ (T a, T b) { return !lt (a, b); }
bool lteq /* <= */ (T a, T b) { return lt (a, b) || eq(a, b); }
bool gt   /* >  */ (T a, T b) { return !lteq (a, b); }


Hier muss man nur die Implementierungen von _Equal und _LessThan überprüfen. Der Rest ist dann trivial.

ao
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
004
07.07.2006, 17:39 Uhr
stephanw
localhorst


Beispiel (MSVC6):

C++:
for (float s=0.1f; s!=1.0f; s+=0.1f)
{
  // == forever :-(
}



Ich habe bisher immer das hier benutzt:


C++:
    template<typename T> bool equal_delta( T a, T b, T delta )
    {
        assert(delta >= 0);
        return ( (b >= (a - delta)) && (b <= (a + delta)) );
    }

    // could be faster than ( ! equal_delta() )
    template<typename T> bool not_equal_delta( T a, T b, T delta )
    {
        assert(delta >= 0);
        return ( (b < (a - delta)) || (b > (a + delta)) );
    }


Aber Eure Gedanken dazu gefallen mir auch gut :-)
--
Reden ist Schweigen und Silber ist Gold.

Dieser Post wurde am 07.07.2006 um 17:39 Uhr von stephanw editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
005
07.07.2006, 17:58 Uhr
(un)wissender
Niveauwart


@ao
Ja habe ich ja auch fast gemacht, geht alles auf eq zurück und dann < >.
Das mit den Potenzen ist wohl wahr, mindesten 10 * epsilon muss es in der Regel bei mir sein.

@stephanw
Jo, das funzt, dein forever. Fein! .
--
Wer früher stirbt ist länger tot.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
006
07.07.2006, 18:11 Uhr
(un)wissender
Niveauwart


So, ich habe es nochmal überarbeitet, es geht alles auf eq und < zurück.
Epsilon wird um eine Zehnerpotenz erhöht.
Hoffentlich sind keine Fehler mehr drin.


C++:
#include <limits>
#include <cmath>

namespace fl
{
    int const factor = 10;
    
    template<typename T>
    bool eq(T first, T second,
        T epsilon = factor * std::numeric_limits<T>::epsilon())
    {
        return std::abs(first - second) < epsilon;
    }  
    
    template<typename T>
    bool neq(T first, T second,
        T epsilon = factor * std::numeric_limits<T>::epsilon())
    {
        return !eq(first, second, epsilon);
    }
    
    template<typename T>
    bool le(T first, T second,
        T epsilon = factor * std::numeric_limits<T>::epsilon())
    {
        return neq(first, second, epsilon) && first < second;
    }

    template<typename T>        
    bool gr (T first, T second,
        T epsilon = factor * std::numeric_limits<T>::epsilon())
    {
        return neq(first, second, epsilon) && !(first < second);
    }    

    template<typename T>    
    bool eqLe (T first, T second,
        T epsilon = factor * std::numeric_limits<T>::epsilon())  
    {
        return !gr(first, second, epsilon);
    }    

    template<typename T>    
    bool eqGr(T first, T second,
        T epsilon = factor * std::numeric_limits<T>::epsilon())
    {
        return !le(first, second, epsilon);
    }    
}


--
Wer früher stirbt ist länger tot.

Dieser Post wurde am 07.07.2006 um 18:13 Uhr von (un)wissender editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
007
07.07.2006, 18:12 Uhr
~stephanw
Gast


@(un)wissender: ja, selbst wenn man auf <= testet, läuft es 1-mal weniger als erwartet. Bei float-Vergleichen sollte man paranoid sein
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
008
07.07.2006, 18:16 Uhr
(un)wissender
Niveauwart


Ich glaube auch. Darum habe ich ja obige templates geschrieben.

§45.7.2
Paranoid zu sein heißt nicht, dass man nicht verfolgt wird.
--
Wer früher stirbt ist länger tot.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
009
08.07.2006, 10:09 Uhr
(un)wissender
Niveauwart


Was sagt man dazu?
Hier wird gesagt, dass Epsilon keine konstante sein darf, sondern abhängig vom Exponenten des zu vergleichenden Wertes abhängt. Stimmt das?
--
Wer früher stirbt ist länger tot.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
Seiten: > 1 < [ 2 ]     [ C / C++ (ANSI-Standard) ]  


ThWBoard 2.73 FloSoft-Edition
© by Paul Baecher & Felix Gonschorek (www.thwboard.de)

Anpassungen des Forums
© by Flo-Soft (www.flo-soft.de)

Sie sind Besucher: