Berechnung in versch. Programmiersprachen ergibt unterschiedliche Ergebnisse? PHP, Python, JavaScript

  • ACHTUNG! SPOILER FÜR AUFGABE AUF ENIGMAGROUP.ORG ENTHALTEN!


    Hallo,
    Ich bin beim Lösen einer JavaScript-Aufgabe auf EnigmaGroup.org auf ein Problem gestoßen. Es geht mir um folgenden Algorithmus:


    PHP:

    PHP
    <?php
    $pass = 1337;
    $year = 158847;
    for ($i = 1; $i <= $year; $i++)
            $pass += $year * $i * $year;
    echo number_format($pass, 0, '', '');
    ?>


    Python:

    Code
    year = 158847
    passw = 1337
    for i in range(1, year + 1):
        passw += (year * i * year)
    print (passw)


    JavaScript:

    Code
    pass = 1337;
    year = 158847;
    for(i=1; i<=year; i++)
        pass += year * i * year;
    alert(pass);


    Komischerweise geben alle drei Sprachen unterschiedliche Ergebnisse aus:

    Code
    PHP:        318338237039211053056
    Python:     318338237039211261689
    JavaScript: 318338237039211050000


    Woran liegt das? Sind die Zahlen einfach so lang, dass sie abgeschnitten werden? Welches Ergebnis ist mathematisch korrekt?


    Edit: Bei Verringerung des Wertes der Variable "year" auf z.B. 200 kommen bei allen 3 Sprachen die gleichen ergebnisse heraus (804001337), bei z.B. 100'000 gibt es zwischen Python und JavaScript schon einen Unterschied.

    Einmal editiert, zuletzt von oernqsvfu_fgvaxg ()

  • Die Zahl ist allgemein zu groß für 32bit Integer.
    In PHP sind Integer automatisch 64bit lang, wenn eine 64bit PHP Version auf einem 64bit Betriebssystem installiert ist (so weit ich weiß, möglicherweise
    hat sich das mittlerweile geändert, NICHT auf Windows, dort immer 32bit).


    Der Javascript Wert ist wohl gerundet, Javascript unterstützt keine echten 64bit Integer.


    Warum die Ergebnisse in PHP und Python verschieden sind weiß ich auf Anhieb nicht, vielleicht kann dir da jemand helfen der mehr über die Sprachen und ihre
    Präzision bei extrem (!) großen Werten weiß.
    Sind wohl irgendwo Präzisions-/Rundungsfehler.


    Edit: Ich habe PHP nie benutzt, ich weiß nicht genau wie das bei PHP mit den Datentypen so läuft. Dieses Zitat hier könnte dir aber helfen:

    Zitat

    You can demonstrate (with Python) that the erroneous values you are getting in PHP is because PHP is promoting to a float when the values are greater than 2**32-1:

    Scheinbar liefert dir Python ein richtiges Ergebnis, während PHP (automatisch) einen Float verwendet und daher (wie bei Gleitkommazahlen erwartet) ungenau rundet
    und JavaScript allgemein zu ungenau ist.

  • Ich hab's nochmal mit 100'000 Jahren nachgerechnet, bei denen das Ergebnis vom Aussehen her mehr Auskunft darüber geben könnte, was denn richtig ist:

    Code
    PHP:        50000499999970803712
    Python:     50000500000000001337
    JavaScript: 50000499999970800000


    In PHP hab ich die Zahl von meinen Raspberry Pi ausrechnen lassen, da ich es nicht auf dem Computer habe. Ich denke mal, dieser kann keine 64 bit Integer berechnen. Das scheint aber eh unwichtig zu sien, da PHP anscheinend generell bei so großen Zahlen Probleme hat.
    Python ist die V3.3.3 in 32 bit - dennoch sieht meiner Meinung nach das Ergebnis hier am realistischsten aus - eventuell wurde hier geschummelt, indem mehrere Integer im RAM allokiert und als einer verwendet wurden? Wie auch immer, hauptsache das Ergebnis stimmt.
    JavaScript sieht meiner Meinung nacht genau so fehlerhaft gerundet aus wie die PHP-Ausgabe.


    Das stimmt ja mit deinen Vermutungen überein, ich danke dir also für die Antwort.


    Edit: Mein Problem war eigentlich, da ich die oben genannte Zahl (158847) mit Python bruteforcen wollte - sie jedoch nie errechnet wurde, weil es eben diese Abweichung gab. Ich frage mich gerade nur, ob sowas denn überhaupt passieren darf. Wenn es einem nicht auffallen würde könnten daraus doch bei komplizierten Berechnungen große Probleme entstehen...

    3 Mal editiert, zuletzt von oernqsvfu_fgvaxg ()

  • Python hat wohl Recht: Die vertrauenswürdige Java Klasse BigInteger errechnet bei 100.000 Jahren genau das Ergebnis,
    das du mit Python rausbekommen hast.



    BigInteger pass=new BigInteger("1337");
    BigInteger year=new BigInteger("100000");
    for(BigInteger i=new BigInteger("1");i.compareTo(year)<=0;i=i.add(BigInteger.ONE))
    {
    pass=pass.add(year.multiply(year.multiply(i)));
    }


    Edit: Mit der ursprünglichen Anzahl von Jahren ergibt die Berechnung "318338237039211261689", auch das stimmt mit deinem Pythonergebnis überein.