web-dev-qa-db-fra.com

Pourquoi l'ajout de deux décimales en Javascript produit-il un mauvais résultat?

Duplicata possible:
Les mathématiques de JavaScript sont-elles cassées?

Pourquoi JS bousille-t-il ce calcul simple?

console.log(.1 + .2)  // 0.3000000000000004
console.log(.3 + .6)  // 0.8999999999999999

Le premier exemple est supérieur au résultat correct, tandis que le second est inférieur. ??? !! Comment réparez-vous ceci? Devez-vous toujours convertir les décimales en nombres entiers avant d'effectuer des opérations? Dois-je seulement me soucier de l'ajout (* et/ne semblent pas avoir le même problème dans mes tests)?

J'ai cherché dans beaucoup d'endroits des réponses. Certains didacticiels (comme les formulaires de panier) prétendent que le problème n'existe pas et ajoutent simplement des valeurs ensemble. Les gourous fournissent des routines complexes pour diverses fonctions mathématiques ou mentionnent que JS "fait un mauvais travail" au passage, mais je n'ai pas encore vu d'explication.

54
Greg Perham

Ce n'est pas un problème JS mais un problème informatique plus général. Les nombres flottants ne peuvent pas stocker correctement tous les nombres décimaux, car ils stockent des éléments en binaire Par exemple:

0.5 is store as b0.1 
but 0.1 = 1/10 so it's 1/16 + (1/10-1/16) = 1/16 + 0.0375
0.0375 = 1/32 + (0.0375-1/32) = 1/32 + 00625 ... etc

so in binary 0.1 is 0.00011... 

mais c'est sans fin. Sauf que l'ordinateur doit s'arrêter à un moment donné. Donc, si dans notre exemple, nous nous arrêtons à 0,00011, nous avons 0,09375 au lieu de 0,1.

Quoi qu'il en soit, cela ne dépend pas de la langue mais de l'ordinateur. Ce qui dépend de la langue, c'est la façon dont vous affichez les nombres. Habituellement, la langue arrondit les nombres à une représentation acceptable. Apparemment, JS ne le fait pas.

Donc ce que vous avez à faire (le nombre en mémoire est suffisamment précis) est juste de dire en quelque sorte à JS d'arrondir "joliment" le nombre lors de leur conversion en texte.

Vous pouvez essayer la fonction sprintf qui vous donne un contrôle précis sur la façon d'afficher un nombre.

33
mb14

De The Floating-Point Guide :

Pourquoi mes chiffres, comme 0,1 + 0,2 ne s'ajoutent-ils pas à un joli rond 0,3, et à la place j'obtiens un résultat étrange comme 0,30000000000000004?

Parce qu'en interne, les ordinateurs utilisent un format (virgule flottante binaire) qui ne peut pas représenter avec précision un nombre comme 0,1, 0,2 ou 0,3.

Lorsque le code est compilé ou interprété, votre "0,1" est déjà arrondi au nombre le plus proche dans ce format, ce qui entraîne une petite erreur d'arrondi avant même le calcul.

Le site contient des explications détaillées ainsi que des informations sur la façon de résoudre le problème (et comment décider s'il s'agit d'un problème dans votre cas).

13
Michael Borgwardt

Ce n'est pas seulement une limitation javascript, elle s'applique à tous les calculs en virgule flottante. Le problème est que 0,1 et 0,2 et 0,3 ne sont pas exactement représentables lorsque javascript (ou C ou Java etc) flotte. Ainsi, la sortie que vous voyez est due à cette inexactitude.

En particulier, seules certaines sommes de deux sont exactement représentables. 0,5 = = 0,1 b = 2 ^ (- 1), 0,25 = 0,01 b = (2 ^ -2), 0,75 = 0,11 b = (2 ^ -1 + 2 ^ -2) sont tous OK. Mais 1/10 = 0,000110001100011..b ne peut être exprimé que comme une somme infinie de puissances de 2, que le langage coupe à un moment donné. C'est ce découpage qui cause ces légères erreurs.

8
Michael Anderson

Ceci est normal pour tous les langages de programmation car toutes les valeurs décimales ne peuvent pas être représentées exactement en binaire. Voir Ce que tout informaticien devrait savoir sur l'arithmétique à virgule flottante

4
Henk Holterman

Cela a à voir avec la façon dont les ordinateurs gèrent les nombres flottants. Vous pouvez en savoir plus à ce sujet ici: http://docs.Sun.com/source/806-3568/ncg_goldberg.html

1
Radu