Je suis en train d'obtenir la somme 1 + 2 + ... + 1000000000
, mais je suis d' obtenir des résultats amusants en PHP et 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
La bonne réponse peut être calculée en utilisant
1 + 2 + ... + n = n(n+1)/2
Bonne réponse = 500000000500000000 , j'ai donc décidé d'essayer une autre langue.
ALLER
var sum , i int64
for i = 0 ; i <= 1000000000; i++ {
sum += i
}
fmt.Println(sum) // 500000000500000000
Mais ça marche bien! Alors, quel est le problème avec mon code PHP et Node.js?
C'est peut-être un problème de langages interprétés, et c'est pourquoi cela fonctionne dans un langage compilé comme Go? Dans l'affirmative, d'autres langages interprétés tels que Python et Perl auraient-ils le même problème?
Réponses:
Python fonctionne:
Ou:
int
Auto de Python se transforme en Pythonlong
qui prend en charge la précision arbitraire. Il produira la bonne réponse sur les plates-formes 32 ou 64 bits.Cela peut être vu en élevant 2 à une puissance bien supérieure à la largeur de bit de la plate-forme:
Vous pouvez démontrer (avec Python) que les valeurs erronées que vous obtenez en PHP sont dues au fait que PHP se transforme en flottant lorsque les valeurs sont supérieures à 2 ** 32-1:
la source
Votre code Go utilise une arithmétique entière avec suffisamment de bits pour donner une réponse exacte. Je n'ai jamais touché PHP ou Node.js, mais d'après les résultats, je soupçonne que les calculs sont effectués en utilisant des nombres à virgule flottante et devraient donc ne pas être exacts pour des nombres de cette ampleur.
la source
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.phpLa raison en est que la valeur de votre variable entière
sum
dépasse la valeur maximale. Et lesum
résultat obtenu est le résultat d'une arithmétique à virgule flottante qui implique l'arrondi. Étant donné que d'autres réponses ne mentionnaient pas les limites exactes, j'ai décidé de l'afficher.La valeur entière maximale pour PHP pour:
Cela signifie donc que vous utilisez un processeur 32 bits ou un système d'exploitation 32 bits ou une version compilée 32 bits de PHP. Il peut être trouvé en utilisant
PHP_INT_MAX
. Lesum
serait calculé correctement si vous le faites sur une machine 64 bits.La valeur entière maximale en JavaScript est 9007199254740992 . La plus grande valeur intégrale exacte avec laquelle vous pouvez travailler est 2 53 (tirée de cette question ). Le
sum
dépasse cette limite.Si la valeur entière ne dépasse pas ces limites, alors vous êtes bon. Sinon, vous devrez rechercher des bibliothèques d'entiers de précision arbitraire.
la source
Voici la réponse en C, pour être complet:
La clé dans ce cas est d'utiliser le
long long
type de données de C99 . Il fournit le plus grand stockage primitif que C puisse gérer et il fonctionne vraiment, très rapidement. Lelong long
type fonctionnera également sur la plupart des machines 32 ou 64 bits.Il y a une mise en garde: les compilateurs fournis par Microsoft ne prennent explicitement pas en charge la norme C99 vieille de 14 ans, donc le faire fonctionner dans Visual Studio est un crapshot.
la source
long long
dans la norme C ++ 11. Il s'agit cependant d'une extension MSVC ++ et g ++ depuis quelques années.movabsq $500000000500000000, %rsi
gcc -O3
ouclang -O3
. Je ne connais pas le nom de l'optimisation spécifique. Fondamentalement, le compilateur remarque que le résultat de la boucle ne dépend d'aucun argument et le calcule au moment de la compilation.Je suppose que lorsque la somme dépasse la capacité d'un natif
int
(2 31 -1 = 2 147 483 647), Node.js et PHP passent à une représentation en virgule flottante et vous commencez à obtenir des erreurs d'arrondi. Un langage comme Go essaiera probablement de conserver une forme entière (par exemple, des entiers 64 bits) aussi longtemps que possible (si, en effet, cela n'a pas commencé par cela). Puisque la réponse tient dans un entier 64 bits, le calcul est exact.la source
Le script Perl nous donne le résultat attendu:
la source
4.99999999067109e+017
sur Perl v5.16.1 MSWin32-x86.bignum
oubigint
. Les deux sont des modules de base, c'est-à-dire qu'ils sont installés avec Perl v5.8.0 ou supérieur. Voirhttp://perldoc.perl.org/bignum.html
ethttp://perldoc.perl.org/bigint.html
La réponse à cette question est "étonnamment" simple:
Tout d'abord - comme la plupart d'entre vous le savent peut-être - un entier 32 bits varie de −2 147 483 648 à 2 147 483 647 . Alors, que se passe-t-il si PHP obtient un résultat, c'est PLUS GRAND que cela?
Habituellement, on s'attendrait à un "débordement" immédiat, entraînant la conversion de 2 147 483 647 + 1 en −2 147 483 648 . Cependant, ce n'est pas le cas. SI PHP rencontre un plus grand nombre, il renvoie FLOAT au lieu de INT.
http://php.net/manual/en/language.types.integer.php
Cela dit, et sachant que l'implémentation de PHP FLOAT suit le format double précision IEEE 754, cela signifie que PHP est capable de gérer des nombres jusqu'à 52 bits, sans perdre en précision. (Sur un système 32 bits)
Ainsi, au point où votre somme atteint 9 007 199 254 740 992 (ce qui est 2 ^ 53 ) La valeur flottante renvoyée par les mathématiques PHP ne sera plus assez précise.
Cet exemple montre le point, où PHP perd de sa précision. Tout d'abord, le dernier bit significatif sera supprimé, ce qui entraînera les 2 premières expressions à donner un nombre égal - ce qu'elles ne sont pas.
À partir de NOW ON, toutes les mathématiques iront mal, lorsque vous travaillez avec des types de données par défaut.
Je ne pense pas. Je pense que c'est un problème de langues qui n'ont aucune sécurité de type. Bien qu'un débordement d'entier comme mentionné ci-dessus se produise dans toutes les langues qui utilisent des types de données fixes, les langues sans sécurité de type peuvent essayer d'attraper cela avec d'autres types de données. Cependant, une fois qu'ils ont atteint leur frontière "naturelle" (donnée par le système) - ils pourraient retourner n'importe quoi, mais le bon résultat.
Cependant, chaque langue peut avoir des threads différents pour un tel scénario.
la source
Les autres réponses ont déjà expliqué ce qui se passe ici (précision en virgule flottante comme d'habitude).
Une solution consiste à utiliser un type entier suffisamment grand, ou à espérer que la langue en choisira un si nécessaire.
L'autre solution consiste à utiliser un algorithme de sommation qui connaît le problème de précision et le contourne. Vous trouverez ci-dessous la même somme, d'abord avec un entier 64 bits, puis avec une virgule flottante 64 bits, puis en utilisant à nouveau la virgule flottante, mais avec l' algorithme de sommation de Kahan .
Écrit en C #, mais il en va de même pour les autres langues.
La sommation Kahan donne un beau résultat. Cela prend bien sûr beaucoup plus de temps à calculer. Votre utilisation dépend a) de vos besoins en termes de performances et de précision, et b) de la manière dont votre langage gère les types de données entiers ou à virgule flottante.
la source
Si vous avez du PHP 32 bits, vous pouvez le calculer avec bc :
En Javascript, vous devez utiliser une bibliothèque de nombres arbitraires, par exemple BigInteger :
Même avec des langages comme Go et Java, vous devrez éventuellement utiliser une bibliothèque de nombres arbitraires, votre numéro était juste assez petit pour 64 bits mais trop élevé pour 32 bits.
la source
En rubis:
Imprime
500000000500000000
, mais prend 4 bonnes minutes sur mon processeur Intel i7 2,6 GHz.Magnuss et Jaunty ont une solution beaucoup plus Ruby:
Pour exécuter un benchmark:
la source
J'utilise node-bigint pour les gros trucs entiers:
https://github.com/substack/node-bigint
Ce n'est pas aussi rapide que quelque chose qui peut utiliser des éléments natifs 64 bits pour ce test exact, mais si vous entrez en plus grand nombre que 64 bits, il utilise libgmp sous le capot, qui est l'une des bibliothèques de précision arbitraire les plus rapides du marché.
la source
a pris des âges en rubis, mais donne la bonne réponse:
la source
Pour obtenir le résultat correct en php, je pense que vous devez utiliser les opérateurs mathématiques de la Colombie-Britannique: http://php.net/manual/en/ref.bc.php
Voici la bonne réponse dans Scala. Vous devez utiliser Longs sinon vous débordez le nombre:
la source
Il y a en fait une astuce sympa à ce problème.
Supposons que c'était 1-100 à la place.
1 + 2 + 3 + 4 + ... + 50 +
100 + 99 + 98 + 97 + ... + 51
= (101 + 101 + 101 + 101 + ... + 101) = 101 * 50
Formule:
Pour N = 100: Sortie = N / 2 * (N + 1)
Pour N = 1e9: Sortie = N / 2 * (N + 1)
C'est beaucoup plus rapide que de parcourir toutes ces données. Votre processeur vous en remerciera. Et voici une histoire intéressante concernant ce problème:
http://www.jimloy.com/algebra/gauss.htm
la source
Cela donne le bon résultat en PHP en forçant le transtypage entier.
la source
Common Lisp est l'un des langages * les plus rapidement interprétés et gère par défaut les entiers arbitrairement grands. Cela prend environ 3 secondes avec SBCL :
la source
Je n'ai pas assez de réputation pour commenter la réponse Common Lisp de @ postfuturist, mais elle peut être optimisée pour se terminer en ~ 500 ms avec SBCL 1.1.8 sur ma machine:
la source
Racket v 5.3.4 (MBP; temps en ms):
la source
Fonctionne bien dans Rebol:
Cela utilisait Rebol 3 qui, bien que compilé en 32 bits, utilise des entiers 64 bits (contrairement à Rebol 2 qui utilisait des entiers 32 bits)
la source
Je voulais voir ce qui s'est passé dans CF Script
J'ai eu 5.00000000067E + 017
Ce fut une expérience assez soignée. Je suis presque sûr que j'aurais pu coder cela un peu mieux avec plus d'effort.
la source
ActivePerl v5.10.1 sous Windows 32 bits, Intel Core2duo 2.6:
résultat: 5.00000000067109e + 017 en 5 minutes.
Avec "use bigint", le script a fonctionné pendant deux heures et aurait fonctionné davantage, mais je l'ai arrêté. Trop lent.
la source
Par souci d'exhaustivité, dans Clojure (beau mais pas très efficace):
la source
AWK:
produit le même mauvais résultat que PHP:
Il semble qu'AWK utilise des virgules flottantes lorsque les nombres sont vraiment grands, donc au moins la réponse est le bon ordre de grandeur.
Essais:
la source
Catégorie autre langage interprété:
Tcl:
Si vous utilisez Tcl 8.4 ou une version antérieure, cela dépend si elle a été compilée avec 32 ou 64 bits. (8.4 est la fin de vie).
Si vous utilisez Tcl 8.5 ou plus récent qui a de grands nombres entiers arbitraires, il affichera le résultat correct.
J'ai mis le test dans un proc pour le compiler en octets.
la source
Pour le code PHP, la réponse est ici :
la source
Port:
Résultats en
500000000500000000
. (sur les deux fenêtres / mingw / x86 et osx / clang / x64)la source
Erlang travaille:
la source
Chose amusante, PHP 5.5.1 donne 499999999500000000 (en ~ 30s), tandis que Dart2Js donne 500000000067109000 (ce qui est normal, car c'est JS qui est exécuté). CLI Dart donne la bonne réponse ... instantanément.
la source
Erlang donne aussi le résultat attendu.
sum.erl:
Et en l'utilisant:
la source
Smalltalk:
la source