T-Rex: sqrt unterschiedliche Ergebnisse auf unterschiedlichen Webseiten

Moin,

ich programmiere gerade eine Spielphysik. Dabei ist eine genaue Berechnung natürlich das A und O. Als Hilfe habe ich mir einen kleinen Unittest gebastelt. Vorher rechne ich etwas im Taschenrechner oder auf Webseite aus, schreibe die erwartete Lösung in den Unittest und hoffe, dass meine Funktion auf das gleiche kommt.

Jetzt gibt es eine Berechnung sqrt( 1 * 1 - 0.89101 * 0.89101) die bei meinem System und bei Google auf 0,45398 ( 0,45398367801056 ) kommt auf anderen Webseiten jedoch auf 0,45399 ( 0,4539904997 ). Ein Rundungsfehler ist es nicht. Ich habe es in JS, PHP, meinem Taschenrechner und auf Google getestet. Es kommt jedesmal das Ergebnis mit der 8 an Position 5. Auf anderen Webseiten kommt das Ergebnis mit der 9 am Ende.

Wie kann sowas sein? Ist das Wurzelziehen nicht eindeutig? Oder gibt es da zwei Methoden? Auf welche kann ich mich verlassen und wenn mein System falsch rechnet, wie kann ich es ihm richtig beibringen?

Fragen über Frage. Vielen Dank fürs lesen und eventuellem Antworten.

Gruß √(T*Rex)

  1. Hallo,

    Jetzt gibt es eine Berechnung sqrt( 1 * 1 - 0.89101 * 0.89101) die bei meinem System und bei Google auf 0,45398 ( 0,45398367801056 ) kommt auf anderen Webseiten jedoch auf 0,45399 ( 0,4539904997 ). Ein Rundungsfehler ist es nicht.

    wie kannst du da so sicher sein? Das ist das erste, was ich als Erklärung angeboten hätte.

    Single Precision Floats mit 32bit nach IEEE 754 haben eine Genauigkeit von etwa 6..7 signifikanten Dezimalstellen. Das würde etwa mit der Größenordnung zusammenpassen, in der deine beobachteten Abweichungen liegen.

    Ich habe es in JS, PHP, meinem Taschenrechner und auf Google getestet. Es kommt jedesmal das Ergebnis mit der 8 an Position 5. Auf anderen Webseiten kommt das Ergebnis mit der 9 am Ende.

    Lass mich raten: Deine lokalen Testmöglichkeiten rechnen mit 64bit oder gar 80bit-Floats, die externen Seiten, die du getestet hast, nur mit 32bit-Floats.

    Wie kann sowas sein? Ist das Wurzelziehen nicht eindeutig?

    Oh, Wurzelziehen (Radizieren) ist sowas von eindeutig. Aber nicht-rationale reelle Zahlen lassen sich eben nicht exakt in einer binären Repräsentation mit begrenzter Stellenzahl darstellen. Selbst manche rationalen Zahlen sind als Binärbruch nicht exakt darstellbar, darunter so einfache wie etwa 1/10. Auch im Dezimalsystem lassen sich ja viele rationale Zahlen nicht exakt darstellen, zum Beispiel 1/7.

    Schönen Abend,
     Martin

    --
    Computer müssen weiblich sein: Eigensinnig, schwer zu durchschauen, immer für Überraschungen gut - aber man möchte sie nicht missen.
  2. Hallo T-Rex,

    die Frage ist nicht, was Rechenfunktionen anderer Webseiten ausgeben. Du programmierst die Spielphysik, es ist also anzunehmen dass die Berechnung in irgendeinem Browser abläuft.

    Mein Browser sagt: 0.4539836780105647

    Wolfram Alpha sagt: 0.453983678010564604483300817185008378509973990295112701693...

    Dem Ergebnis von Wolfram würde ich unbedingt Vertrauen schenken.

    Wenn irgendwer 0,4539904997 sagt, ist die Berechnung ungenau. Vielleicht wurde mit float statt double gerechnet. Ob es garantiert ist, dass JS mit double rechnet, weiß ich nicht - aber du könntest deinem Spiel ein paar Prüfberechnungen vorschalten und wenn in irgendeinem Browser ein Wert jenseits der erwarteten Toleranz herauskommt, könntest Du den Start verweigern.

    Andererseits - 0,4539904997 vs 0.4539836780105647, das ist ein Tausendstel Prozent. Bist Du sicher, dass Dir diese Abweichung Probleme macht? Ggf. musst Du Dir stabilere Algorithmen überlegen, die auch mit schlechterer Genauigkeit brauchbare Ergebnisse liefern.

    Rolf

    --
    sumpsi - posui - clusi
    1. Vielen Dank euch zwei. Das klingt beides sehr plausibel.

      @Rolf B: Ich habe es noch nicht ausprobiert, denke aber, dass die Abweichung tatsächlich ein Problem ist. Auf dieser Minimalen und zuerst total nichtigen Abweichung bauen die anderen Berechnungen auf. Ergo verschlimmert sich die Abweichung pro Frame - eventuell.

      Das ist so als ob man einen Ball in den Weltraum schießt um einen Stern zu treffen. Wirft man am Anfang 0,00001 mm daneben mag das total nichtig sein. Am Ende verfehlt man den Stern um Tausende Kilometer.

      Kann ich meinen eigenen Browser in einen 32bit Modus setzen?

      Gruß T-Rey (32 Bit Zeichencodierungsfehler)

      1. Hallo T-Rex,

        Kann ich meinen eigenen Browser in einen 32bit Modus setzen?

        Nein. Wenn du mit diesem Problem umgehen möchtest, dann bleibt dir letztlich nur runden auf eine bestimmte Genauigkeit, etwa immer dann, wenn du die Zahl anzeigst, z.B. mit toFixed() oder toPrecision(). Es gäbe da auch noch decimal.js.

        Wegen genau solcher Probleme lernt man in den Grundlagen, dass man Floats nicht direkt vergleicht, sondern nur prüft, ob die Differenz kleiner ε ist 😉

        const x = 0.3;
        const y = 0.2;
        const z = 0.1;
        
        console.log(x - y - z == 0);
        console.log(Math.abs(x - y - z) < Number.EPSILON);
        

        LG,
        CK

        1. Ahoi,

          sofern ich mein System (Browser) nicht irgendwie in den 32 Bit Modus bekomme, muss ich blindlings einer Bibliothek vertrauen. Das mag ich nicht. Selbst mein Handy scheint 64 bit zu haben. Muss ich wirklich mein olles Notebook von vor 10 Jahren aus dem Keller holen?

          Gruß 64 BiT-Rex

          1. Hallo T-Rex,

            sofern ich mein System (Browser) nicht irgendwie in den 32 Bit Modus bekomme, muss ich blindlings einer Bibliothek vertrauen. Das mag ich nicht.

            Du kannst dich auch darin üben, fremden Quelltext zu lesen und dir die Bibliothek zu Gemüte führen 😀

            Selbst mein Handy scheint 64 bit zu haben. Muss ich wirklich mein olles Notebook von vor 10 Jahren aus dem Keller holen?

            Wenn es darum geht, nur deinen eigenen Browser als 32-Bit-Variante laufen zu lassen, dann gibt es für z.B. Firefox auch noch 32-Bit-Versionen, siehe das Kleingedruckte „Erweiterte Installationsoptionen & andere Plattformen“ auf der Download-Seite.

            Das ist aber leider keine Lösung für dein Problem, sondern nur Symptom-Bekämpfung. Floating-Point-Arithmetik ist ungenau, und wenn es dich bei deinem konkreten Anwendungsfall im 32-Bit-Modus nicht beisst, dann beisst es dich später an anderer Stelle. Du wirst leider lernen müssen, damit umzugehen.

            LG,
            CK

          2. sofern ich mein System (Browser) nicht irgendwie in den 32 Bit Modus bekomme, muss ich blindlings einer Bibliothek vertrauen. Das mag ich nicht. Selbst mein Handy scheint 64 bit zu haben. Muss ich wirklich mein olles Notebook von vor 10 Jahren aus dem Keller holen?

            JavaScript kennt nur 64-bit Floats, jedes davon abweichende Verhalten wäre ein Browserbug. Auf 32-bit Systemen, die keine native 64-bit Floating Arithmetik unterstützen, wird das Verhalten simuliert auf Kosten von Prozessor-Zyklen. Es gäbe theoretisch auch noch arbitrary precision integers, aber für deine Spielephysik sind die vermutlich ungeeignet.

            1. Also jetzt mal im Ganzen...

              Ich programmiere eine Spielphysik für Billard. Das Spiel soll aber nicht auf einem System laufen, sondern via Mulitplayer auf mehreren Systemen (Smartphones). Man muss gewisse Informationen übertragen, damit ein Stoß auch beim Gegner ausgeführt werden kann.

              Ignorieren wir mal sonder Sachen wie die weiße Kugel nach einem Foul neu setzen oder Spielende, so sind es lediglich X-Vektor, Y-Vektor und Energie des Stoßes der weißen Kugel. Der Rest sollte auf beiden Systemen gleich berechnet werden.

              Zur Sicherheit gibt es zwar einen Abgleich aller Kugelpositionen nach Berechnung des Stoßes und der Animationen (das heißt, beide Systeme wieder auf den gleichen Stand bringen), bei falsch Berechnung würden dann aber die Kugel hüpfen. Im schlimmsten Fall führt eine falsche Berechnung sogar dazu, dass eine Kugel via Animation in einem Loch verschwindet und sie nach Abgleich der Kugelposition doch wieder auf dem Tisch erscheint. Sowas möchte ich wenn möglich umgehen.

              Die Option alle Frames zu übertragen möchte mein Auftraggeber nicht. Es soll Traffic gespart werden.

              Wenn Javascript immer im gleichen System Rechnet (32,64,128,"mir egal" Bit), ist für mich alles super. Dann springen die Kugeln nicht. Ob die Kugel nach dem Stoß minimal anders läuft als sie sollte ist mir dann auch ziemlich egal, solang sie garantiert auf beiden Geräten "falsch" läuft.

              Sollte Javascript auf zwei Geräten zu unterschiedlichen Ergebnissen kommen, dann würde ich dies gerne mal mit eigenen Augen sehen, testen und dann mittels der decimal "Bibliothek" korrigieren - dies aber auch wieder testen bzw. sehen, dass es korrigiert wurde.

              Also gibt es für mich zwei weitere Verfahrensmöglichkeiten.

              1. Javascript arbeitet wie du sagst immer im 64 Bit Modus - dann ist alles super!
              2. Javascript arbeitet in unterschiedlichen Bit Modi - dann würde ich gerne wissen, wie ich das einmal sehen kann. Gerne mit Browser Erweiterung, Smartphone App oder wie auch immer

              Gruß T-Rex, der bringt den Stein ins Rollen!

              1. Hallo T-Rex,

                Ignorieren wir mal sonder Sachen wie die weiße Kugel nach einem Foul neu setzen oder Spielende, so sind es lediglich X-Vektor, Y-Vektor und Energie des Stoßes der weißen Kugel.

                Besser wäre, den Impuls zu verwenden. Was für ein schönes Billard aber fehlt, ist ein Drehimpuls der Bälle. Bei dir würden sich die Bälle nur gradlinig bewegen.

                Bis demnächst
                Matthias

                --
                Pantoffeltierchen haben keine Hobbys.
                ¯\_(ツ)_/¯
            2. Hallo 1unitedpower,

              JavaScript kennt nur 64-bit Floats

              Ja, das kann man nachlesen. Aber wie ist das mit den Operationen? Die Intel FPU hat zumindest früher mal intern mit 80-bit Registern gerechnet. Muss man damit rechnen, dass es bei Intel, AMD oder Qualcomm Hardware unterschiedliche Ergebnisse bei Float-Operationen gibt?

              Man könnte nun einfach mal anfangen und davon ausgehen, das 64-bittig gerechnet wird und alle beteiligten FPUs exakt gleich werkeln. Und dann kann man im Test (mit möglichst vielen Geräten unterschiedlicher Hersteller) schauen, um wieviel die berechnete Position der Geräte voneinander abweicht.

              Man kann aber auch nach jedem Rechenschritt die Kugelpositionen auf vier Stellen runden (heißt: wenn man die Koordinaten von 0-9999 festlegt, entfernt man einfach die Nachkommastellen). Damit hat man zumindest Mikroabweichungen im Griff. Die Angaben zu Geschwindigkeit und Drall sollte man ebenfalls entsprechend runden.

              Der Witz ist ja: Es fällt niemandem auf, wenn die simulierte Realität nicht ganz exakt ist. Im Gegenteil, eigentlich müsste bei jedem Bandenanprall ein winziger Zufallsfehler hinzugefügt werden, denn Banden sind niemals völlig exakt gefertigt. Sicherlich wird dein Modell auch aus Gründen der Performance vereinfacht sein. Wenn eine Billardkugel mit einem gewissen Impuls angestoßen wird, dann wird sie kurz gleiten und dann erst durch die Reibung mit dem Tuch anfangen zu rollen. Wenn eine rollende Kugel eine andere anstößt, überträgt sie Drehimpuls. Die weiße Kugel kann schräg angestoßen worden sein, so dass sie mit einer Drehung um die Z-Achse ankommt, was sich auf die angespielte Kugel überträgt - wenn Du das exakt haben willst, löst Du vermutlich Differenzialgleichungssysteme. Das wirst Du nicht wollen, du hast ein vereinfachtes Modell, und da fällt es gar nicht auf, wenn Du potenzielle Fehler wegrundest.

              Die Idee, vor Spielstart auf den Clients ein paar Teststöße mit bekannten Ergebnissen rechnen zu lassen, könnte trotzdem noch nützlich sein.

              Rolf

              --
              sumpsi - posui - clusi
              1. JavaScript kennt nur 64-bit Floats

                Ja, das kann man nachlesen. Aber wie ist das mit den Operationen? Die Intel FPU hat zumindest früher mal intern mit 80-bit Registern gerechnet. Muss man damit rechnen, dass es bei Intel, AMD oder Qualcomm Hardware unterschiedliche Ergebnisse bei Float-Operationen gibt?

                Die Operationen sind auch im IEEE-754-Standard festgelegt. Eine Engine, die da Kovertierungen vornimmt, die das Ergebnis verfälschen, ist schlicht und simpel fehlerhaft. In der Praxis weiß ich auch von keiner Engine, die sich da abweichend vom Standard verhält.

                Man kann aber auch nach jedem Rechenschritt die Kugelpositionen auf vier Stellen runden (heißt: wenn man die Koordinaten von 0-9999 festlegt, entfernt man einfach die Nachkommastellen). Damit hat man zumindest Mikroabweichungen im Griff. Die Angaben zu Geschwindigkeit und Drall sollte man ebenfalls entsprechend runden.

                Das verschlimmert es nur, man büßt unnötigerweise Präzision ein. Wenn man diesen Weg beschreiten möchte, müsste man auf eine fixed-point Arithmetik wechseln, um einen positiven Effekt zu haben, aber so einen Datentypen kennt JS nicht.

                Der Witz ist ja: Es fällt niemandem auf, wenn die simulierte Realität nicht ganz exakt ist.

                Kommt darauf an, ein instablier Algorithmus kann durchaus schwere Fehler, wie Auslöschung zur Folge haben.

              2. Hallo,

                Wenn eine rollende Kugel eine andere anstößt, überträgt sie Drehimpuls. Die weiße Kugel kann schräg angestoßen worden sein, so dass sie mit einer Drehung um die Z-Achse ankommt, was sich auf die angespielte Kugel überträgt -

                Echt? Ich gucke gerne mal Snooker, dass die weiße Kugel Effet hat, kommt natürlich häufig vor, aber die angestoßenen rollen doch linear los.

                Oder ist das bei anderen Billardsorten anders?

                Gruß
                Kalk

                1. n'Abend,

                  Wenn eine rollende Kugel eine andere anstößt, überträgt sie Drehimpuls.

                  ja, zumindest einen Teil.

                  Die weiße Kugel kann schräg angestoßen worden sein, so dass sie mit einer Drehung um die Z-Achse ankommt, was sich auf die angespielte Kugel überträgt

                  Genau. Aber auch wenn die weiße keinen Effet hat und eine andere Kugel etwas außerhalb der Mitte trifft, wird ein Teil der Energie als Drehimpuls übertragen.

                  Echt? Ich gucke gerne mal Snooker, dass die weiße Kugel Effet hat, kommt natürlich häufig vor, aber die angestoßenen rollen doch linear los.

                  Das kommt dir so vor. Meist ist der Effekt auch so minimal, dass es nicht auffällt.

                  Besonders spektakuär finde ich, wenn Profis die weiße Kugel kräftig unterhalb der Mitte anstoßen, so dass sie a) über eine andere Kugel hinwegspringt und b) so viel horizontalen Effet bekommt, dass sie nach dem Landen quasi wendet und die eben übersprungene Kugel rückwärts wieder anstößt.

                  Das habe ich selbst aber noch nie geschafft.

                  Ciao,
                   Martin

                  --
                  Computer müssen weiblich sein: Eigensinnig, schwer zu durchschauen, immer für Überraschungen gut - aber man möchte sie nicht missen.
                  1. Besonders spektakuär finde ich, wenn Profis die weiße Kugel kräftig unterhalb der Mitte anstoßen, so dass sie a) über eine andere Kugel hinwegspringt und b) so viel horizontalen Effet bekommt, dass sie nach dem Landen quasi wendet und die eben übersprungene Kugel rückwärts wieder anstößt.

                    Dito!

                    Das habe ich selbst aber noch nie geschafft.

                    Vorsicht beim Üben! So einen Tisch neu beziehen lassen, kann ins Geld gehen 😉

                  2. Das habe ich selbst aber noch nie geschafft. Martin

                    Ähm ... ich möchte bitte nur Antworten von Profis.

                    Gruß Der Billard König T-Rex

                    1. Hallo T-Rex,

                      Das habe ich selbst aber noch nie geschafft.

                      Ähm ... ich möchte bitte nur Antworten von Profis.

                      ymmd

                      Bis demnächst
                      Matthias

                      --
                      Pantoffeltierchen haben keine Hobbys.
                      ¯\_(ツ)_/¯