Rundungsfehler?



  • Hallo!

    Ich bastel gerade an einer Geradenklasse. (Also ne Klasse für mathematische Geraden ^^) und habe nun folgendes Problem bei der Vergleiche-2-Geraden-Funktion:

    bool straight::equal(straight& S)
    {
        //Ich möchte wissen ob die Geraden (mehr oder weniger) identisch sind:
        //1: Ist Richtungsvektor 1 ein vielfaches von Richtungsvektor 2?
        float quotient = richtungsvektor.get(x)/S.richtungsvektor.get(x); //x ist enum für 0
        for(int i=1; i<3; ++i){
            float buf = richtungsvektor.get(i)/S.richtungsvektor.get(i);
            cout << buf << " - " << quotient << " = " << buf - quotient << endl;
            if(buf != quotient) return false;
        }
    
        //2: Liegt Aufpunkt 1 auf der Geraden 2?
        // ...
    }
    

    Die Ausgabe bei 2 eigendlich gleichen Vektoren sagt nun wohl alles:
    Die Richtungsvektoren sind ein vielfaches des jeweils anderen.

    0.2 - 0.2 = 2.98023e-08

    //Da bricht dann eben alles ab, da ja (buf != quotient)

    Komisch nur dass genau 0.2 ausgegeben wird. 0.2 - 0.2 dürfte doch auch genau 0 sein und nicht irgendeine Annäherung daran ...

    Danke euch 🙂



  • Das hängt mit der Darstellung der floats im Zweiersystem zusammen udn damit dass floats nur endliche Genauigkeit haben. Wenn du beispielsweise im Zehnersystem mit drei Stellen Genauigkeit rechnest und den Bruch 2/3 auf zwei verschiedene Arten ausrechnest, bekommst du evtl. einmal 0.665 raus udn einmal 0.667 - die Differenz wäre dann nicht 0.



  • pumuckl schrieb:

    Das hängt mit der Darstellung der floats im Zweiersystem zusammen udn damit dass floats nur endliche Genauigkeit haben. Wenn du beispielsweise im Zehnersystem mit drei Stellen Genauigkeit rechnest und den Bruch 2/3 auf zwei verschiedene Arten ausrechnest, bekommst du evtl. einmal 0.665 raus udn einmal 0.667 - die Differenz wäre dann nicht 0.

    Hmmm ... bei mir ist ja die differenz extrem klein... wie löse ich das Problem denn am besten?! Bei den beiden Geraden handelt es sich ja um 2 identische ... und das möchte ich auch gerne mathematisch bestätigt bekommen 😞



  • std::abs(int) bzw. std::fabs(float) liefert dir den Abstand des übergebenen Wertes zu Null. Das kannst du für deine Berechnung verwenden:

    if(std::fabs(0.2f-0.2f) < ein_kleiner_wert)
    {
    ...
    }
    

    ein_kleiner_wert ist dabei irgendein sehr kleiner float-Wert denn du freiwählen kannst. (Je kleiner, desto genauer ist das Ergebnis. Aber bei einem zu kleinen Wert funktioniert deine Berechnung nicht mehr.



  • Ah ich verstehe... fabs ist dann sowas wie der Betrag von x? Ich danke sehr 🙂



  • Ja.

    cheers, Swordfish



  • Gerade bei mathematischen Problemen würde ich dir allerdings raten, genauere Typen zu nehmen. Also double sollte mindestens drin liegen, sonst kannst du auch zusätzliche Bibliotheken benutzen 😉



  • naja mit double wäre es genau dasselbe trotzdem

    gleitkommazahlen bringen einfach immer eine kleine ungenauigkeit mit sich und man muss es eben so wie hier bereits beschreieben wurde mit einer toleranz für die ungenauigkeit wieder gut machen



  • Vevusio schrieb:

    naja mit double wäre es genau dasselbe trotzdem

    Klar kann man den Rundungsfehler damit nicht wegbekommen. Aber mit double können immerhin doppelt so viele Informationen wie mit float gespeichert werden. Dadurch kann man auch den Toleranzbereich weiter einschränken und die Wahrscheinlichkeit, dass zwei unterschiedliche Geraden als gleich gelten, ist kleiner.



  • Wenn die beiden Vektoren nicht aus unmittelbaren Kopien entstanden sind, sondern irgendwie berechnet worden sind, so können sie "beliebig ungenau" sein. Je nach dem wie diese Berechnung ausgesehen hat.
    Das lässt sich auch durch den Wechsel von float nach double nicht wirklich verbessern.

    Die Frage ist: wie weit dürfen zwei Vektoren a und b voneinander abweichen, um noch als linear abhängig durchzugehen.
    Um das von der Länge der Vektoren unabhängig zu machen, würde ich empfehlen, den Winkel oder zumindest den Sinus des Winkels zu berechnen. Unterschreitet dieser ein bestimmtes Mass so sind sie 'gleich'.

    Der Sinus lässt sich in 3D durch das Kreuzprodukt bestimmen;
    sin( winkel a,b ) = a X b / ( abs(a) * abs(b) ).
    (siehe dazu auch die Vektorklasse hier)

    Liegt der Sinus bei ca.0, so ist der Winkel entweder auch ca.0° oder ca.180°, was in beiden Fällen eine lineare Abhängiglkeit bedeutet.

    Gruß
    Werner



  • Werner Salomon schrieb:

    Wenn die beiden Vektoren nicht aus unmittelbaren Kopien entstanden sind, sondern irgendwie berechnet worden sind, so können sie "beliebig ungenau" sein. Je nach dem wie diese Berechnung ausgesehen hat.
    Das lässt sich auch durch den Wechsel von float nach double nicht wirklich verbessern.

    Die Frage ist: wie weit dürfen zwei Vektoren a und b voneinander abweichen, um noch als linear abhängig durchzugehen.
    Um das von der Länge der Vektoren unabhängig zu machen, würde ich empfehlen, den Winkel oder zumindest den Sinus des Winkels zu berechnen. Unterschreitet dieser ein bestimmtes Mass so sind sie 'gleich'.

    Der Sinus lässt sich in 3D durch das Kreuzprodukt bestimmen;
    sin( winkel a,b ) = a X b / ( abs(a) * abs(b) ).
    (siehe dazu auch die Vektorklasse hier)

    Liegt der Sinus bei ca.0, so ist der Winkel entweder auch ca.0° oder ca.180°, was in beiden Fällen eine lineare Abhängiglkeit bedeutet.

    Gruß
    Werner

    Das hört sich ausgesprochen gut an! 🙂 Ich probier die Winkelberechnng gleich mal aus.

    Meine Klasse ist in wirklichkeit ein template, allerdings muss ich auch ein bisschen auf Performance achten und bei Experimenten mit double dauert alles schon deutlich länger ...


Anmelden zum Antworten