Parce 0.1
est pas 0,1; cette valeur n'est pas représentable en double précision, il s'arrondi au nombre double précision, ce qui est exactement:
0.1000000000000000055511151231257827021181583404541015625
Lorsque vous appelez fmod
, vous obtenez le reste de la division par la valeur indiquée ci-dessus, ce qui est exactement:
0.0999999999999999500399638918679556809365749359130859375
qui arrondit à 0.1
(ou peut-être 0.09999999999999995
) lors de l'impression.
En d'autres termes, fmod
fonctionne parfaitement, mais vous ne lui donnez pas la contribution que vous pensez être.
Edit: votre propre implémentation vous donne la bonne réponse, car il est moins précis , croyez-le ou non. Tout d'abord, notez que fmod
calcule le reste sans erreur d'arrondi; la seule source d'inexactitude est l'erreur de représentation introduite en utilisant la valeur 0.1
. Passons maintenant à votre implémentation et voyons comment l'erreur d'arrondi qu'elle provoque corrige exactement l'erreur de représentation.
Évaluer a - floor(a/n) * n
une étape à la fois, garder la trace des valeurs exactes calculées à chaque étape:
D'abord, nous évaluons 1.0/n
, où n
est le plus proche approximation double précision à 0.1
comme indiqué ci-dessus. Le résultat de cette division est d'environ:
9.999999999999999444888487687421760603063276150363492645647081359...
Notez que cette valeur est pas un nombre double précision représentable - il obtient arrondi.Pour voir comment cela se passe-t arrondi, regardons le nombre en binaire au lieu de décimales:
1001.1111111111111111111111111111111111111111111111111 10110000000...
L'espace indique où l'arrondi à double précision se produit. Étant donné que la partie après le point rond est plus grande que le point de mi-parcours exact, cette valeur arrondit exactement à 10
.
floor(10.0)
est, de façon prévisible, 10.0
. Tout ce qui reste est de calculer 1.0 - 10.0*0.1
.
En binaire, la valeur exacte de 10.0 * 0.1
est:
1.0000000000000000000000000000000000000000000000000000 0100
à nouveau, cette valeur est représentable comme un double, et ainsi est arrondi à la position indiquée par un espace. Cette fois, il arrondit à exactement 1.0
, et donc le calcul final est 1.0 - 1.0
, qui est bien sûr 0.0
.
Votre implémentation contient deux erreurs d'arrondi, qui annulent exactement l'erreur de représentation de la valeur 0.1
dans ce cas. fmod
, en revanche, est toujours exacte (au moins sur les plates-formes avec une bonne bibliothèque numérique), et expose l'erreur de représentation de 0.1
.
renvoie ici 0,09999999999999995 (OS X, Python 2.5.4). La réponse dépend de la bibliothèque C de votre plateforme. – geoffspear
ok, désolé j'ai arrondi. de toute façon, la réponse devrait être 0 dans ma compréhension puisque 1.0/0.1 = 10 qui est un nombre entier. – beemtee