Parallele Vektoren und 'float'?



  • Aus der Schule ist bekannt, dass man, um festzustellen, ob zwei Vektoren zueinander parallel sind, herausfinden muss, ob einer der beiden Vektoren ein 'Vielfaches' des anderen ist.

    Also v1 und v2 sind demnach parallel, wenn gilt

    v1*scalar==v2; // scalar ist irgend eine float-Zahl
    

    Wenn ich das so schreibe:

    struct Vector{
     float x,y,z;
    };
    
    Vector A(2.0f,1.0f,3.0f);
    Vector B(6.0f,3.0f,9.0f); 
    
    // Hier versuche ich den ||-Operator so zu überladen, weil er wie das
    // aus der Schule bekannten Symbol für 'parallel' aussieht
    bool operator||(const Vector& op1, const Vector& op2){
    
    float scalar=op1.x/op2.x; // Zuerst den Scalar herausfinden
    
    return op1.x*scalar==op2.x && op1.y*scalar==op2.y && op1.z*scalar==op2.z;
    }
    

    Soweit, so gut. Leider treten mit floats derartige Rundungsfehler auf, dass die Funktion operator|| oft false zurückliefert, auch wenn die Vektoren ganz offensichtlich parallel sein müssten. Andererseits müssen aber floats verwendet werden (weil das die 3D-API so haben möchte!).
    Vielleicht kennt jemand noch ein alternatives Verfahren, das zuverlässiger ist, um die Vektoren auf Parallelität zu prüfen.

    Danke für Tipps!



  • Um zwei float-Werte zu vergleichen solltest Du nicht == verwenden, sondern eher prüfen, ob die Differenz klein ist:

    statt a==b liber abs(a-b) < epsilon, dabei ist epsilon der kleinste positive Wert, den Du als von 0 verschieden akzeptierst.



  • Jester schrieb:

    Um zwei float-Werte zu vergleichen solltest Du nicht == verwenden, sondern eher prüfen, ob die Differenz klein ist:

    statt a==b liber abs(a-b) < epsilon, dabei ist epsilon der kleinste positive Wert, den Du als von 0 verschieden akzeptierst.

    thx ich probiers so!


  • Mod

    Mathe-Laie schrieb:

    float scalar=op1.x/op2.x; // Zuerst den Scalar herausfinden
    

    Das geht in der Form schief, wenn op2.x gleich 0 ist.

    Mathe-Laie schrieb:

    return op1.x*scalar==op2.x && op1.y*scalar==op2.y && op1.z*scalar==op2.z;
    

    Soweit, so gut. Leider treten mit floats derartige Rundungsfehler auf, dass die Funktion operator|| oft false zurückliefert, auch wenn die Vektoren ganz offensichtlich parallel sein müssten.

    Du testest floats mit == auf Gleichheit. Das ist zwar in gewisser Weise wohldefiniert, aber wie du festgestellt hast ist es nicht dasselbe wie das mathematische "ist gleich". Was du bei floats brauchst, ist ein Gleichheitstest der folgenden Form:

    bool is_equal(float a, float b) {
      return fabs(a-b) < epsilon;
    }
    

    wobei epsilon deine akzeptierte "Ungenauigkeit" darstellt.

    Du bekommst bei Fließkommazahlen unvermeidbar Rundungsfehler. Wenn du zwei Vektoren hast, die fast parallel sind (d.h. der eingeschlossene Winkel ist sehr klein, aber nicht 0), dann wird es passieren, dass deine Funktion fälschlicherweise "die sind parallel" zurückliefert. Je nachdem wie du epsilon wählst, beeinflusst du die Wahrscheinlichkeit eines false positives bzw. eines false negatives. Die genaue Wahl von epsilon hängt von deinem Anwendungsfall ab und auch davon, woher die Vektoren stammen, also welche Genauigkeit du zu diesem Zeitpunkt überhaupt noch erwarten kannst.



  • Was sollte der Operator|| (prüft, ob zwei Vektoren parallel sind) zurückgeben, falls irgendein x-beliebiger Vektor (etwa (3,2,0)) mit dem Nullvektor verglichen wird (also mit (0,0,0)).
    Die Vielfachenregel zum Nachweis der Parallelität funktioniert hier nur in eine Richtung, nämlich von (3,2,0) * 0 = (0,0,0).
    Umgekehrt klappt es nicht.
    Ist jetzt der Nullvektor parallel zu beliebigen Vektoren oder nicht (ist das etwa eine Geschmackssache, oder irgendwo definiert?).

    Danke für Aufklärung, ob false oder true in solchen Fällen zurückgegeben werden soll!



  • Muss es nicht auch

    is_equal(op2.x*scalar, op1.x) && is_equal(op2.y*scalar, op1.y) && is_equal(op2.z*scalar, op1.z);
    

    heißen?

    Wenn
    \vec{a}=\[ \left( \begin{array}{c} 2 \\ 1 \\ 3 \end{array} \right) \]
    und
    \vec{b}=\[ \left( \begin{array}{c} 6 \\ 3 \\ 9 \end{array} \right) \]
    würde
    s=x_ax_b=26=13s=\frac{x\_a}{x\_b}=\frac{2}{6}=\frac{1}{3}
    ergeben.
    Aber
    13xa=236\frac{1}{3}x_a=\frac{2}{3}\neq6
    Also muss es wohl
    13x_b=63=2=x_a\frac{1}{3}x\_b=\frac{6}{3}=2=x\_a
    heißen.

    Auch wenn ich etwas übersehe, kack drauf, wollte schon immer mal mit Latex rumspielen 😃



  • LaTeX rul3z!11 schrieb:

    Muss es nicht auch

    is_equal(op2.x*scalar, op1.x) && is_equal(op2.y*scalar, op1.y) && is_equal(op2.z*scalar, op1.z);
    

    heißen?

    Wenn man es genau betrachtet - ja.

    Und generell dürfte es nötig sein, bei deinem Vergleich auf "Division by zero" zu kontrollieren. Da ist es wohl besser, die Division gleich komplett wegzulassen, als alle möglichen Sonderfälle abzufangen:

    is_equal(op1.x*op2.y,op2.x*op1.y) && is_equal(op1.x*op2.z,op2.x*op1.z)
    

    x_ax_b=y_ay_b\frac{x\_a}{x\_b}=\frac{y\_a}{y\_b} ist äquivalent zu x_ay_b=x_by_ax\_a * y\_b = x\_b * y\_a



  • Mathe-Laie schrieb:

    Was sollte der Operator|| (prüft, ob zwei Vektoren parallel sind) zurückgeben, falls irgendein x-beliebiger Vektor (etwa (3,2,0)) mit dem Nullvektor verglichen wird (also mit (0,0,0)).
    Die Vielfachenregel zum Nachweis der Parallelität funktioniert hier nur in eine Richtung, nämlich von (3,2,0) * 0 = (0,0,0).
    Umgekehrt klappt es nicht.
    Ist jetzt der Nullvektor parallel zu beliebigen Vektoren oder nicht (ist das etwa eine Geschmackssache, oder irgendwo definiert?).

    Danke für Aufklärung, ob false oder true in solchen Fällen zurückgegeben werden soll!

    es wäre imho geschickter, zu benutzen, dass zwei vektoren genau dann parallel sind, wenn das kreuzprodukt null ist. das erspart dir nämlich das umständliche teilen. (0,0,0) ist zu allen vektoren parallel.



  • Würd ich auch sagen, entweder mit Kreuzprodukt oder du rechnest den Winkel zw den Vektoren aus, wenn der 0 ist sind sie auch parallel.
    Bzw. musst du gar nicht den Winkel ausrechnen, sondern nur,wenn

    a,b ... Vektoren

    (a * b)/(|a| * |b|) = 1 -> Vektoren sind parallel

    recht schnell, sogar ohne Wurzel kannst du das so machen

    (a.x * b.x + a.y * b.y + a.z *b.z)*(a.x * b.x + a.y * b.y + a.z *b.z) / ((a.x*a.x +a.y*a.y +a.z*a.z )(b.x*b.x +b.y*b.y +b.zb.z ) = 1 -> Vektoren Parallel


Anmelden zum Antworten