2009-09-20 7 views
6

Pourquoi ce code 7.30 - 7.20 dans ruby ​​renvoie 0.0999999999999996, et non 0.10?Arithmétique en rubis

Mais si je vais écrire 7.30 - 7.16, par exemple, tout ira bien, je vais obtenir 0.14.

Quel est le problème, et comment puis-je le résoudre?

Répondre

1

C'est une erreur courante de la façon dont les nombres à virgule flottante sont représentés en mémoire.

Utilisez BigDecimal si vous avez besoin de résultats exacts.

result=BigDecimal.new("7.3")-BigDecimal("7.2") 
puts "%2.2f" % result 
+0

Il y a une assez bonne discussion à ce sujet sur: http://whynotwiki.com/Ruby_/_Numbers –

+2

Notez que BigDecimal ne donne des résultats exacts que si le nombre a un nombre fini de chiffres dans base 10. – sepp2k

+0

http://floating-point-gui.de/ –

3

Le problème est que floating point is inaccurate. Vous pouvez le résoudre en utilisant Rational, BigDecimal ou tout simplement des nombres entiers (par exemple, si vous voulez stocker de l'argent, vous pouvez stocker le nombre de cents comme un int au lieu du nombre de dollars comme un flottant). BigDecimal peut stocker précisément n'importe quel nombre qui a un nombre fini de chiffres dans les nombres de base 10 et de tours qui ne le font pas (donc trois tiers ne sont pas un entier).

Rational peut stocker avec précision n'importe quel nombre rationnel et ne peut pas stocker du tout de nombres irrationnels.

0

Puisque vous faites des calculs en virgule flottante, le nombre renvoyé est celui que votre ordinateur utilise pour la précision.

Si vous voulez une réponse plus proche, à une précision d'ensemble, il suffit de multiplier le flottant par (par exemple 100), de le convertir en int, de faire le calcul, puis de diviser.

Il existe d'autres solutions, mais je trouve que c'est le plus simple car arrondir me semble toujours un peu incertain.

Cela a été posée ici, vous voudrez peut-être chercher quelques-unes des réponses données avant, comme celui-ci: Dealing with accuracy problems in floating-point numbers

6

Le problème est que certains chiffres, nous pouvons facilement écrire en décimal n'ont pas une représentation exacte dans le format à virgule flottante particulier mis en œuvre par le matériel actuel. Une manière informelle d'indiquer ceci est que tous les entiers le font, mais pas toutes les fractions, parce que nous stockons normalement la fraction avec un exposant 2**e. Donc, vous avez 3 choix:

  1. Arrondir de manière appropriée. Le résultat non arrondi est toujours vraiment très proche, donc un résultat arrondi est invariablement "parfait". C'est ce que fait Javascript et beaucoup de gens ne réalisent même pas que JS fait tout en virgule flottante.

  2. Utilisez l'arithmétique à virgule fixe. Ruby rend vraiment cela vraiment facile. C'est l'un des seuls langages qui passent de Fixnum à Class Bignum au fur et à mesure que les nombres grossissent.

  3. Utilisez une classe qui est conçu pour résoudre ce problème, comme BigDecimal

Pour regarder le problème plus en détail, nous pouvons essayer de représenter votre « 7.3 » en binaire. La partie 7 est facile, 111, mais comment faisons-nous .3? 111.1 est 7.5, trop grand, 111.01 est 7.25, se rapprochant.Il s'avère que 111.010011 est le "prochain plus petit nombre le plus proche", 7.296875, et quand nous essayons de remplir le .003125 manquant finalement nous découvrons qu'il est juste 111.010011001100110011 ... pour toujours, non représentable dans notre encodage choisi dans une chaîne de bits finie .

1

Il est intéressant de noter qu'un nombre qui a peu de décimales dans une base peut typiquement avoir un très grand nombre de décimales dans une autre. Par exemple, il faut un nombre infini de décimales pour exprimer 1/3 (= 0.3333 ...) dans la base 10, mais seulement une décimale dans la base 3. De même, il faut beaucoup de décimales pour exprimer le nombre 1/10 (= 0.1) dans la base 2.