Ich versuche , die Summe zu bekommen 1 + 2 + ... + 1000000000
, aber ich bin immer lustig Ergebnisse in PHP und Node.js .
PHP
$sum = 0;
for($i = 0; $i <= 1000000000 ; $i++) {
$sum += $i;
}
printf("%s", number_format($sum, 0, "", "")); // 500000000067108992
Node.js
var sum = 0;
for (i = 0; i <= 1000000000; i++) {
sum += i ;
}
console.log(sum); // 500000000067109000
Die richtige Antwort kann mit berechnet werden
1 + 2 + ... + n = n(n+1)/2
Richtige Antwort = 500000000500000000 , also habe ich beschlossen, eine andere Sprache auszuprobieren.
GEHEN
var sum , i int64
for i = 0 ; i <= 1000000000; i++ {
sum += i
}
fmt.Println(sum) // 500000000500000000
Aber es funktioniert gut! Was ist also falsch an meinem PHP- und Node.js-Code?
Vielleicht ist dies ein Problem interpretierter Sprachen, und deshalb funktioniert es in einer kompilierten Sprache wie Go? Wenn ja, hätten andere interpretierte Sprachen wie Python und Perl das gleiche Problem?
Antworten:
Python funktioniert:
Oder:
Pythons
int
Auto wird zu einem Pythonlong
hochgestuft, der beliebige Genauigkeit unterstützt. Auf 32- oder 64-Bit-Plattformen wird die richtige Antwort ausgegeben.Dies kann gesehen werden, indem 2 auf eine Leistung angehoben wird, die weit größer als die Bitbreite der Plattform ist:
Sie können (mit Python) demonstrieren, dass die fehlerhaften Werte, die Sie in PHP erhalten, darauf zurückzuführen sind, dass PHP zu einem Float hochgestuft wird, wenn die Werte größer als 2 ** 32-1 sind:
quelle
Ihr Go-Code verwendet eine Ganzzahlarithmetik mit genügend Bits, um eine genaue Antwort zu geben. Nie PHP oder Node.js berührt, aber aufgrund der Ergebnisse vermute ich, dass die Berechnung mit Gleitkommazahlen durchgeführt wird und daher erwartet werden sollte, dass sie für Zahlen dieser Größenordnung nicht genau sind.
quelle
If PHP encounters a number beyond the bounds of the integer type, it will be interpreted as a float instead. Also, an operation which results in a number beyond the bounds of the integer type will return a float instead.
- php.net/manual/en/language.types.integer.phpDer Grund ist, dass der Wert Ihrer Ganzzahlvariablen
sum
den Maximalwert überschreitet. Und das, wassum
Sie erhalten, ist das Ergebnis einer Gleitkomma-Arithmetik, bei der abgerundet wird. Da in anderen Antworten die genauen Grenzen nicht erwähnt wurden, habe ich beschlossen, sie zu veröffentlichen.Der maximale ganzzahlige Wert für PHP für:
Dies bedeutet, dass Sie entweder eine 32-Bit-CPU oder ein 32-Bit-Betriebssystem oder eine 32-Bit-kompilierte Version von PHP verwenden. Es kann mit gefunden werden
PHP_INT_MAX
. Dassum
würde korrekt berechnet, wenn Sie es auf einem 64-Bit-Computer tun.Der maximale ganzzahlige Wert in JavaScript beträgt 9007199254740992 . Der größte exakte Integralwert, mit dem Sie arbeiten können, ist 2 53 (aus dieser Frage entnommen ). Das
sum
überschreitet diese Grenze.Wenn der ganzzahlige Wert diese Grenzwerte nicht überschreitet, sind Sie gut. Andernfalls müssen Sie nach Ganzzahlbibliotheken mit beliebiger Genauigkeit suchen.
quelle
Hier ist der Vollständigkeit halber die Antwort in C:
Der Schlüssel in diesem Fall ist die Verwendung des
long long
Datentyps von C99 . Es bietet den größten primitiven Speicher, den C verwalten kann, und läuft sehr, sehr schnell. Derlong long
Typ funktioniert auch auf den meisten 32- oder 64-Bit-Computern.Es gibt eine Einschränkung: Von Microsoft bereitgestellte Compiler unterstützen den 14 Jahre alten C99-Standard ausdrücklich nicht. Daher ist es ein Crapshot, diesen in Visual Studio auszuführen.
quelle
long long
in den C ++ 11-Standard aufgenommen. Es ist jedoch seit einigen Jahren eine MSVC ++ - und g ++ - Erweiterung.movabsq $500000000500000000, %rsi
gcc -O3
oderclang -O3
. Ich kenne den Namen der spezifischen Optimierung nicht. Grundsätzlich stellt der Compiler fest, dass das Ergebnis der Schleife von keinem Argument abhängt, und berechnet es zur Kompilierungszeit.Ich vermute, wenn die Summe die Kapazität eines nativen Systems überschreitet
int
(2 31 -1 = 2.147.483.647), wechseln Node.js und PHP zu einer Gleitkommadarstellung und es treten Rundungsfehler auf. Eine Sprache wie Go wird wahrscheinlich versuchen, so lange wie möglich bei einer Ganzzahlform (z. B. 64-Bit-Ganzzahlen) zu bleiben (wenn dies tatsächlich nicht damit begonnen hat). Da die Antwort in eine 64-Bit-Ganzzahl passt, ist die Berechnung genau.quelle
Perl-Skript geben uns das erwartete Ergebnis:
quelle
4.99999999067109e+017
unter Perl v5.16.1 MSWin32-x86.bignum
oderbigint
. Beide sind Kernmodule, dh sie werden mit Perl v5.8.0 oder höher installiert. Siehehttp://perldoc.perl.org/bignum.html
undhttp://perldoc.perl.org/bigint.html
Die Antwort darauf ist "überraschend" einfach:
Erstens reicht - wie die meisten von Ihnen vielleicht wissen - eine 32-Bit-Ganzzahl von –2.147.483.648 bis 2.147.483.647 . Was passiert also, wenn PHP ein Ergebnis erzielt, das GRÖSSER ist?
Normalerweise würde man einen sofortigen "Überlauf" erwarten, wodurch 2.147.483.647 + 1 zu -2.147.483.648 werden . Dies ist jedoch NICHT der Fall. Wenn PHP auf eine größere Zahl stößt, wird FLOAT anstelle von INT zurückgegeben.
http://php.net/manual/en/language.types.integer.php
Das heißt, und zu wissen, dass die Implementierung von PHP FLOAT dem IEEE 754-Format mit doppelter Genauigkeit folgt, bedeutet, dass PHP in der Lage ist, Zahlen bis zu 52 Bit zu verarbeiten, ohne an Genauigkeit zu verlieren. (Auf einem 32-Bit-System)
An dem Punkt, an dem Ihre Summe 9.007.199.254.740.992 erreicht (das sind 2 ^ 53 ), ist der von PHP Maths zurückgegebene Float-Wert nicht mehr genau genug.
Dieses Beispiel zeigt den Punkt, an dem PHP an Genauigkeit verliert. Zuerst wird das letzte signifikante Bit gelöscht, wodurch die ersten beiden Ausdrücke zu einer gleichen Anzahl führen - was sie nicht sind.
Ab JETZT wird die gesamte Mathematik schief gehen, wenn mit Standarddatentypen gearbeitet wird.
Das glaube ich nicht. Ich denke, dies ist ein Problem von Sprachen, die keine Typensicherheit haben. Während ein Integer-Überlauf wie oben erwähnt in jeder Sprache auftritt, die feste Datentypen verwendet, versuchen die Sprachen ohne Typensicherheit möglicherweise, dies mit anderen Datentypen abzufangen. Sobald sie jedoch ihre "natürliche" (vom System vorgegebene) Grenze erreicht haben, können sie alles zurückgeben, aber das richtige Ergebnis.
Jede Sprache kann jedoch unterschiedliche Threadings für ein solches Szenario haben.
quelle
Die anderen Antworten erklärten bereits, was hier passiert (Gleitkommapräzision wie gewohnt).
Eine Lösung besteht darin, einen ganzzahligen Typ zu verwenden, der groß genug ist, oder zu hoffen, dass die Sprache bei Bedarf einen auswählt.
Die andere Lösung besteht darin, einen Summationsalgorithmus zu verwenden, der das Präzisionsproblem kennt und es umgeht. Unten finden Sie dieselbe Summierung, zuerst mit 64-Bit-Ganzzahl, dann mit 64-Bit-Gleitkomma und dann wieder mit Gleitkomma, jedoch mit dem Kahan-Summierungsalgorithmus .
Geschrieben in C #, aber das Gleiche gilt auch für andere Sprachen.
Die Kahan-Summe ergibt ein schönes Ergebnis. Die Berechnung dauert natürlich viel länger. Ob Sie es verwenden möchten, hängt a) von Ihren Leistungs- und Präzisionsanforderungen ab und b) davon, wie Ihre Sprache mit Ganzzahl- und Gleitkomma-Datentypen umgeht.
quelle
Wenn Sie 32-Bit-PHP haben, können Sie es mit bc berechnen :
In Javascript müssen Sie eine beliebige Zahlenbibliothek verwenden , zum Beispiel BigInteger :
Selbst bei Sprachen wie Go und Java müssen Sie möglicherweise eine beliebige Nummernbibliothek verwenden. Ihre Nummer war zufällig klein genug für 64-Bit, aber zu hoch für 32-Bit.
quelle
In Ruby:
Druckt
500000000500000000
, dauert aber auf meinem 2,6 GHz Intel i7 gut 4 Minuten.Magnuss und Jaunty haben eine viel mehr Ruby-Lösung:
So führen Sie einen Benchmark durch:
quelle
Ich benutze Node-Bigint für große Ganzzahlen:
https://github.com/substack/node-bigint
Es ist nicht so schnell wie etwas, das native 64-Bit-Inhalte für diesen genauen Test verwenden kann. Wenn Sie jedoch größere Zahlen als 64-Bit verwenden, wird libgmp unter der Haube verwendet, eine der schnelleren Bibliotheken mit beliebiger Genauigkeit.
quelle
hat ewig in Rubin gedauert, gibt aber die richtige Antwort:
quelle
Um das richtige Ergebnis in PHP zu erhalten, müssten Sie vermutlich die BC-Mathematikoperatoren verwenden: http://php.net/manual/en/ref.bc.php
Hier ist die richtige Antwort in Scala. Sie müssen Longs verwenden, sonst überlaufen Sie die Nummer:
quelle
Es gibt tatsächlich einen coolen Trick zu diesem Problem.
Angenommen, es war stattdessen 1-100.
1 + 2 + 3 + 4 + ... + 50 +
100 + 99 + 98 + 97 + ... + 51
= (101 + 101 + 101 + 101 + ... + 101) = 101 * 50
Formel:
Für N = 100: Ausgang = N / 2 * (N + 1)
Für N = 1e9: Ausgabe = N / 2 * (N + 1)
Dies ist viel schneller als das Durchlaufen all dieser Daten. Ihr Prozessor wird es Ihnen danken. Und hier ist eine interessante Geschichte zu diesem Problem:
http://www.jimloy.com/algebra/gauss.htm
quelle
Dies ergibt das richtige Ergebnis in PHP, indem die Ganzzahlumwandlung erzwungen wird.
quelle
Common Lisp ist eine der am schnellsten interpretierten * Sprachen und verarbeitet beliebig große Ganzzahlen standardmäßig korrekt. Dies dauert bei SBCL ca. 3 Sekunden :
quelle
Ich habe nicht genug Ruf, um die Common Lisp-Antwort von @ postfuturist zu kommentieren, aber sie kann so optimiert werden, dass sie mit SBCL 1.1.8 auf meinem Computer in ~ 500 ms abgeschlossen ist:
quelle
Racket v 5.3.4 (MBP; Zeit in ms):
quelle
Funktioniert gut in Rebol:
Hierbei wurde Rebol 3 verwendet, das trotz seiner 32-Bit-Kompilierung 64-Bit-Ganzzahlen verwendet (im Gegensatz zu Rebol 2, bei dem 32-Bit-Ganzzahlen verwendet wurden).
quelle
Ich wollte sehen, was in CF Script passiert ist
Ich habe 5.00000000067E + 017
Dies war ein ziemlich ordentliches Experiment. Ich bin mir ziemlich sicher, dass ich das mit mehr Aufwand ein bisschen besser hätte codieren können.
quelle
ActivePerl v5.10.1 unter 32-Bit-Fenstern, Intel Core2duo 2.6:
Ergebnis: 5.00000000067109e + 017 in 5 Minuten.
Mit "use bigint" funktionierte das Skript zwei Stunden lang und würde mehr funktionieren, aber ich habe es gestoppt. Zu langsam.
quelle
Der Vollständigkeit halber in Clojure (schön, aber nicht sehr effizient):
quelle
AWK:
erzeugt das gleiche falsche Ergebnis wie PHP:
Es scheint, dass AWK Gleitkomma verwendet, wenn die Zahlen wirklich groß sind, also ist zumindest die Antwort die richtige Größenordnung.
Testläufe:
quelle
Kategorie andere interpretierte Sprache:
Tcl:
Wenn Sie Tcl 8.4 oder älter verwenden, hängt es davon ab, ob es mit 32 oder 64 Bit kompiliert wurde. (8.4 ist das Ende des Lebens).
Wenn Sie Tcl 8.5 oder eine neuere Version mit beliebigen großen Ganzzahlen verwenden, wird das richtige Ergebnis angezeigt.
Ich habe den Test in einen Prozess eingefügt, um ihn bytekompiliert zu bekommen.
quelle
Für den PHP-Code lautet die Antwort hier :
quelle
Hafen:
Ergebnisse in
500000000500000000
. (unter Windows / Mingw / x86 und OSX / Clang / x64)quelle
Erlang arbeitet:
quelle
Lustige Sache, PHP 5.5.1 gibt 499999999500000000 (in ~ 30s), während Dart2Js 500000000067109000 gibt (was zu erwarten ist, da es JS ist, das ausgeführt wird). CLI Dart gibt die richtige Antwort ... sofort.
quelle
Erlang gibt auch das erwartete Ergebnis.
sum.erl:
Und damit:
quelle
Smalltalk:
quelle