Rechnungen liefert unterschiedliche Werte



  • Hllo Leute,

    Ich hab ein Programm dass ich neu schreibe. Ich habe es dabei auch gleich auf 64bit kompiliert und nicht wie ursprünglich 32. Nun habe ich ein Problem, das mich verwirrt. Ich habe folgende Codezeile:

    #include <cmath>
    
    //irgendwelcher code
    
    void foo(){
    //macht was
    
        Fit.push_back(flux_arr[0] * sin(flux_arr[1] * x[i] + flux_arr[3]) + flux_arr[2]);
    
    //macht noch mehr
    }
    

    in beiden Programmen ist diese Codezeile dieselbe, die Ergebnisse allerdings nicht. Die Ergebnisse sehen folgendermaßen aus:

    F(ursprünglich) = 95.060725762140351
    F(jetzt) = 95.060725762140137

    Der Unterschied ist nicht groß aber da und und zieht sich auch in die Ergebnisse nachfolgender Rechnungen. Ich habs mit dem Debugger überprüft und die Eingangsparameter haben dieselben werte und dieselben Typen in beiden Fällen. Die sin() Funktion liefert aber einen abweichenden Wert in den letzten vier Stellen. Wenn ich die Rechnung Schritt für Schritt mache ist das ergebnis nach der sin() Funktion:

    Erg1 = -0.46585698983812873
    Erg2 = -0.46585698983813151

    Woher kann das kommen? Kann das an dem 32 zu 64bit dingens liegen, obwohl ich nicht verstehe wieso das sein sollte. Oder liegt das an den mathebibliotheken? Oder übersehe ich einfach was?

    Erstellt wurde beides mit Visual Studio 2012.



  • x86 Prozessoren haben eine Gleitkomaeinheit die intern genauer rechnet als die übliche Gleitkommazahlbreite. Diese hat auch eine integrierte Sinus-Berechnung. Auf x86-64 wird aber für Gleitkommaberechnungen die SSE-Einheit benutzt die auch intern mit derselben Genauigkeit rechnet. Für die Berechnung des Sinus wird hier ein Software-Verfahren verwendet. Ich denke daher kommt es zu leicht unterschiedlichen Ergebnissen.



  • Wie kommst du an die Zahlen? Konsolenausgabe, Debugger ... ?

    In der Defaulteinstellung benutzt der gcc im 64 Bit Modus SSE, während er im 32 Bit Modus die FPU benutzt. Das kann zu verschiedenen Werten führen. Keine Ahnung wie das bei VS aussieht.

    Der 64 Bit Modus hat mehr/andere Register. Das könnte sich auch hier auswirken.



  • TNA schrieb:

    x86 Prozessoren haben eine Gleitkomaeinheit die intern genauer rechnet als die übliche Gleitkommazahlbreite. Diese hat auch eine integrierte Sinus-Berechnung. Auf x86-64 wird aber für Gleitkommaberechnungen die SSE-Einheit benutzt die auch intern mit derselben Genauigkeit rechnet. Für die Berechnung des Sinus wird hier ein Software-Verfahren verwendet. Ich denke daher kommt es zu leicht unterschiedlichen Ergebnissen.

    Ich führe beides auf demselben Rechner aus der einen x64 prozessor hat. Muss damit dieser Unterschied sichtbar wird der Prozessor physisch existieren oder ist das eine compilerinterne Einstellung, was nun verwendet wird?

    Im Endeffekt gibt es für mich keine Möglichkeit diese Abweichung zu korrigieren solange ich weiterhin auf 64bit kompiliere? Oder kann ich den Compiler anweisen die FPU zu verwenden?

    Meine Ergebnisse kommen übrigens aus dem Debugger.


  • Mod

    was passiert, wenn die Rechnung in einzelne Schritte unterteilt wird, bei denen jeweils das Ergebnis zwischengespeichert wird? also

    double a = flux_arr[1] * x[i];
    double b = a + flux_arr[3];
    double c = sin(b);
    double d = flux_arr[0] * c;
    double e = d + flux_arr[2];
    Fit.push_back(e);
    

    Vorausgesetzt, es werden keine zu aggressiven Matheoptimierungen vorgenommen (fastmath o.ä. ist tabu), sollte dies stets zum gleichen exakten Ergebnis führen.



  • camper schrieb:

    was passiert, wenn die Rechnung in einzelne Schritte unterteilt wird, bei denen jeweils das Ergebnis zwischengespeichert wird? also

    double a = flux_arr[1] * x[i];
    double b = a + flux_arr[3];
    double c = sin(b);
    double d = flux_arr[0] * c;
    double e = d + flux_arr[2];
    Fit.push_back(e);
    

    Vorausgesetzt, es werden keine zu aggressiven Matheoptimierungen vorgenommen (fastmath o.ä. ist tabu), sollte dies stets zum gleichen exakten Ergebnis führen.

    wie gesagt hab ich das bereits gemacht. Daher komm ich auch darauf, dass die sin() Funktion zur Abweichung führt. Das Zeug davor macht alles wie erwartet.



  • Freddy_Kay schrieb:

    TNA schrieb:

    x86 Prozessoren haben eine Gleitkomaeinheit die intern genauer rechnet als die übliche Gleitkommazahlbreite. Diese hat auch eine integrierte Sinus-Berechnung. Auf x86-64 wird aber für Gleitkommaberechnungen die SSE-Einheit benutzt die auch intern mit derselben Genauigkeit rechnet. Für die Berechnung des Sinus wird hier ein Software-Verfahren verwendet. Ich denke daher kommt es zu leicht unterschiedlichen Ergebnissen.

    Ich führe beides auf demselben Rechner aus der einen x64 prozessor hat. Muss damit dieser Unterschied sichtbar wird der Prozessor physisch existieren oder ist das eine compilerinterne Einstellung, was nun verwendet wird?

    Der Prozessor hat sozusagen einen getrennten Befehlssatz der für 64-Bit Code zum Einsatz kommt. Also eine Compilereinstellung zusammen mit dem Passenden Betriebssystem.


  • Mod

    Es wäre zweckmäßig, mal nachzurechnen, welcher Wert korrekt ist.
    Ab dem Pentium garantiert Intel für die trigonometrischen Funktionen eine Abweichung von maximal 1 (binären) Stelle, sofern das Argument nicht reduziert werden musste (wenn keine errata greifen, FDIV grüsst...)
    Also liegt möglicherweise ein Fehler in der Software für 64-bit vor - evtl. muss die auch erst konfiguriert werden; häufig benötigt man ja nicht die volle Genauigkeit.

    Edit: kurze Suche führt u.a. zu http://ofekshilon.com/2014/02/01/x86x64-library-numerical-differences/

    Wäre interessant zu prüfen, ob die Situation mit glibc besser aussieht.



  • camper schrieb:

    Es wäre zweckmäßig, mal nachzurechnen, welcher Wert korrekt ist.
    Ab dem Pentium garantiert Intel für die trigonometrischen Funktionen eine Abweichung von maximal 1 (binären) Stelle, sofern das Argument nicht reduziert werden musste (wenn keine errata greifen, FDIV grüsst...)
    Also liegt möglicherweise ein Fehler in der Software für 64-bit vor - evtl. muss die auch erst konfiguriert werden; häufig benötigt man ja nicht die volle Genauigkeit.

    Edit: kurze Suche führt u.a. zu http://ofekshilon.com/2014/02/01/x86x64-library-numerical-differences/

    Wäre interessant zu prüfen, ob die Situation mit glibc besser aussieht.

    ich bin mir nicht ganz sicher, wie ich herausfinde welcher Wert korrekt ist. Mim Taschenrechner wirds schwierig, Windows Taschenrechner und wolfram alpha gibt mir:

    -0,46585698972300337924483906930641

    was nochmal deutlich anders ist als die beiden anderen Werte.


Log in to reply