web-dev-qa-db-fra.com

Comparaison des nombres en virgule flottante Python

Je suis en train de passer en revue quelques notions de base de Python et la comparaison des nombres en virgule flottante pose un problème épineux.

2.2 * 3.0 == 6.6
3.3 * 2.0 == 6.6

Je pensais que tous deux devraient retourner un False. Cependant, le deuxième m'a donné un vrai. enter image description here

S'il vous plaît aidez-moi ici. Merci!

12
Zhiya

Cela pourrait être éclairant:

>>> float.hex(2.2 * 3.0)
'0x1.a666666666667p+2'
>>> float.hex(3.3 * 2.0)
'0x1.a666666666666p+2'
>>> float.hex(6.6)
'0x1.a666666666666p+2'

Bien qu'ils soient tous affichés en décimal sous la forme 6.6, lorsque vous inspectez la représentation interne, deux d'entre eux sont représentés de la même manière, tandis que l'un d'entre eux ne l'est pas.

13
Amadan

Afin de compléter la bonne réponse d’Amadan, voici une façon plus évidente de voir que 2.2 * 3. et 3,3 * 2. ne sont pas représentés par le même float: dans un shell Python,

>>> 2.2 * 3.
6.6000000000000005
>>> 3.3 * 2.
6.6

En fait, le shell Python affiche la représentation des nombres, ce qui par définition devrait permettre au flottant correspondant d'être construit correctement à partir de la représentation, de sorte que vous voyez l'approximation numérique 2.2 * 3 de Python. Le fait que 2.2 * 3. ! = 3.3 * 2. est évident quand on voit tous les chiffres nécessaires, comme ci-dessus.

3
Eric O Lebigot

Il est également évident que 3.3 * 2.0 est numériquement identique à 6.6. Ce dernier calcul n’est rien d’autre qu’un incrément de l’exposant binaire, c’est le résultat d’une multiplication par une puissance de deux. Vous pouvez le voir dans ce qui suit:

    | s exponent    significant
----+-------------------------------------------------------------------
1.1 | 0 01111111111 0001100110011001100110011001100110011001100110011010
2.2 | 0 10000000000 0001100110011001100110011001100110011001100110011010
3.3 | 0 10000000000 1010011001100110011001100110011001100110011001100110
6.6 | 0 10000000001 1010011001100110011001100110011001100110011001100110

Ci-dessus, vous voyez la représentation binaire des nombres à virgule flottante 3.3 et 6.6. La seule différence entre les deux nombres est l'exposant puisqu'ils ne sont multipliés que par deux. Nous savons que IEEE-754 va:

  • approximer un nombre décimal avec la plus petite erreur numérique
  • peut représenter tous les entiers jusqu'à 2^53 exactement (pour binary64)

Donc, puisque 2.0 est exactement représentable, une multiplication avec ce nombre ne sera rien de plus qu'un changement d'exposant. Ainsi, tous les éléments suivants créeront les mêmes nombres à virgule flottante:

6.6 == 0.825 * 16.0 == 1.65 * 4.0 == 3.3*2.0 == 13.2 * 0.5 == ...

Cela signifie-t-il que 2.2*3.0 est différent de 6.6 en raison de son importance? Non, c'était simplement dû à des erreurs d'arrondis dans la multiplication.

Un exemple où cela aurait fonctionné aurait été 5.5*2.0 == 2.2*5.0 == 11.0. Ici l'arrondi était favorable

0
kvantour