2009-05-31 1 views
8

Je travaille quotidiennement avec Python 2.4 dans mon entreprise. J'ai utilisé la fonction logarithm polyvalente 'log' de la bibliothèque mathématique standard, et quand j'ai entré le journal (2 ** 31, 2) il a retourné 31.000000000000004, ce qui m'a paru étrange.Logarithme inexact en Python

J'ai fait la même chose avec d'autres puissances de 2, et cela a fonctionné parfaitement. J'ai couru 'log10 (2 ** 31)/log10 (2)' et j'ai obtenu un tour 31.0

J'ai essayé d'exécuter la même fonction d'origine dans Python 3.0.1, en supposant qu'il a été corrigé dans une version plus avancée.

Pourquoi cela se produit-il? Est-il possible qu'il y ait des inexactitudes dans les fonctions mathématiques en Python?

+0

double de la question du point Floating pérenne (pourquoi je reçois des erreurs à virgule flottante), ne peut pas trouver le meilleur double Q pour poster, peut-être que quelqu'un d'autre le peut. –

+0

Je dois préciser que Python 3 n'a * pas * corrigé l'erreur de virgule flottante. Au lieu de cela, la sortie d'impression utilise un algorithme intelligent pour afficher la valeur de virgule flottante souhaitée, au lieu du mou. –

Répondre

44

Ceci est à prévoir avec l'arithmétique de l'ordinateur. C'est en suivant des règles particulières, telles que IEEE 754, qui ne correspondent probablement pas aux mathématiques que vous avez apprises à l'école.

Si cette réellement fait questions, utilisez Python decimal type.

Exemple:

from decimal import Decimal, Context 
ctx = Context(prec=20) 
two = Decimal(2) 
ctx.divide(ctx.power(two, Decimal(31)).ln(ctx), two.ln(ctx)) 
+22

+1 pour une bonne réponse, mais surtout pour "Si cela compte vraiment." Les sondes ont été acheminées vers Saturne avec moins de précision. – dwc

+0

En effet. L'italique est la partie la plus importante de la réponse. –

+0

@dwc Cela aurait été important si OP avait pris le plafond du résultat de la fonction log. Alors l'erreur deviendrait très grande. Dans mon cas, dans un de mes programmes, je faisais ceci: 'a = floor (log (x, b))' et le programme plantait quelques lignes en avant parce que 'floor (log (243,3))' était sortir pour être 4 – Rushil

17

toujours supposer que opérations en virgule flottante aura une erreur en eux et vérifier l'égalité en prenant cette erreur en compte (soit une valeur en pourcentage comme 0,00001% ou une valeur fixe comme ,00000000001). Cette imprécision est donnée car tous les nombres décimaux ne peuvent pas être représentés en binaire avec une précision de nombre de bits fixe.

Votre cas particulier n'en fait pas partie si Python utilise IEEE754 puisque 31 devrait être facilement représentable même avec une seule précision. Il est possible cependant qu'il perde de la précision dans l'une des nombreuses étapes qu'il faut pour calculer le log , simplement parce qu'il n'a pas de code pour détecter des cas particuliers comme une puissance directe de deux.

+1

+1 Je pense que vous avez une poignée sur elle avec votre dernière phrase. –

+0

Très intéressant. J'écris du code depuis très longtemps, et c'est la première fois que je suis tombé sur ce phénomène. Mais après la réponse ici, je pense que maintenant je commence à voir la plus grande image de pourquoi cela arrive. –

5

Les opérations en virgule flottante ne sont jamais exactes. Ils renvoient un résultat qui présente une erreur relative acceptable pour l'infrastructure linguistique/matérielle.

En général, il est faux de supposer que les opérations en virgule flottante sont précises, en particulier en simple précision. "Accuracy problems" section de Wikipedia article à virgule flottante :)

2

Ceci est normal. Je m'attendrais à ce que log10 soit plus précis que log (x, y), car il sait exactement quelle est la base du logarithme, il peut aussi y avoir un support matériel pour calculer les logarithmes de base 10.

3

numéros doubles à virgule flottante IEEE ont 52 bits of precision. Depuis 10^15 < 2^52 < 10^16, un double a entre 15 et 16 chiffres significatifs. Le résultat 31.000000000000004 est correct à 16 chiffres, donc c'est aussi bon que vous pouvez vous attendre.

1

Le repr esentation (float.__repr__) d'un certain nombre de python tente de retourner une chaîne de chiffres comme proche de la valeur réelle que possible lorsqu'elle est convertie en retrait, étant donné que l'arithmétique IEEE-754 est précis dans la limite.Dans tous les cas, si vous print ed le résultat, vous ne remarquerez pas:

>>> from math import log 
>>> log(2**31,2) 
31.000000000000004 
>>> print log(2**31,2) 
31.0 

print convertit ses arguments à cordes (dans ce cas, par la méthode float.__str__), qui répond à l'inexactitude en affichant moins de chiffres :

>>> log(1000000,2) 
19.931568569324174 
>>> print log(1000000,2) 
19.9315685693 
>>> 1.0/10 
0.10000000000000001 
>>> print 1.0/10 
0.1 

usuallyuseless' réponse est très utile, en fait :)