Les nombres à virgule flottante, 𝐟𝐥𝐨𝐚𝐭 , sont représentés en binaire dans les ordinateurs, ils ne peuvent donc pas représenter exactement les mêmes valeurs que les nombres décimaux. Cela peut conduire à des résultats inattendus lors de la comparaison de nombres à virgule flottante.
Consultez l’article suivant sur les fonctions NumPy 𝐧𝐩.𝐢𝐬𝐜𝐥𝐨𝐬𝐞() et 𝐧𝐩.𝐚𝐥𝐥𝐜𝐥𝐨𝐬𝐞() .
Notes sur la comparaison des nombres à virgule flottante
Les nombres à virgule flottante, 𝐟𝐥𝐨𝐚𝐭 , sont représentés en binaire à l’intérieur des ordinateurs, ils ne peuvent donc pas représenter exactement les mêmes valeurs que les nombres décimaux.
Par exemple, 𝐩𝐫𝐢𝐧𝐭(0.1) renvoie 0.1 . Cette sortie est cependant une approximation. Python arrondit la représentation binaire réelle de 0.1 à un nombre gérable de décimales pour l’affichage. Si vous augmentez le nombre de chiffres avec 𝐟𝐨𝐫𝐦𝐚𝐭() , vous constaterez peut-être qu’elle contient en fait une erreur.
print(0.1) # 0.1 print(format(0.1, '.20f')) # 0.10000000000000000555
Consultez la documentation officielle pour plus de détails.
Dans de nombreux cas, il n’y a pas lieu de s’inquiéter de telles erreurs. Cependant, elles peuvent conduire à des résultats inattendus, notamment lors de la comparaison des résultats d’opérations avec des nombres à virgule flottante.
print(0.1 + 0.1 + 0.1) # 0.30000000000000004 print(0.1 + 0.1 + 0.1 == 0.3) # False
print((19 / 155) * (155 / 19)) # 0.9999999999999999 print((19 / 155) * (155 / 19) == 1) # False
Par exemple, vous pouvez utiliser 𝐫𝐨𝐮𝐧𝐝() pour arrondir, ou 𝐚𝐛𝐬() pour comparer la valeur absolue de la différence avec une valeur suffisamment petite.
- Arrondir les nombres avec round() et Decimal.quantize() en Python
- Obtenir la valeur absolue avec abs() et math.fabs() en Python
print(round(0.1 + 0.1 + 0.1, 10) == round(0.3, 10)) # True print(abs((0.1 + 0.1 + 0.1) - 0.3) < 1e-10) # True
En Python, 1𝐞<𝐧𝐮𝐦𝐛𝐞𝐫> est une façon d’exprimer des nombres à virgule flottante en notation scientifique. Ici, 1𝐞<𝐧𝐮𝐦𝐛𝐞𝐫> représente 10 à la puissance <𝐧𝐮𝐦𝐛𝐞𝐫> . Il est important de noter que le 𝐞 utilisé ici est un symbole d’exponentiation, et non le nombre d’Euler.
print(1e5) # 100000.0 print(1e-3) # 0.001
Vérifiez si les deux nombres à virgule flottante sont proches : 𝐦𝐚𝐭𝐡.𝐢𝐬𝐜𝐥𝐨𝐬𝐞()
Vous pouvez écrire une comparaison de nombres à virgule flottante comme les exemples ci-dessus simplement en utilisant la fonction 𝐢𝐬𝐜𝐥𝐨𝐬𝐞() dans le module 𝐦𝐚𝐭𝐡 .
Comme indiqué ci-dessous, 𝐦𝐚𝐭𝐡.𝐢𝐬𝐜𝐥𝐨𝐬𝐞(𝐚, 𝐛) renvoie T𝐫𝐮𝐞 si 𝐚 et 𝐛 sont approximativement égaux.
import math print(math.isclose(0.1 + 0.1 + 0.1, 0.3)) # True print(math.isclose((19 / 155) * (155 / 19), 1)) # True
Spécifiez les tolérances : 𝐫𝐞𝐥_𝐭𝐨𝐥 , 𝐚𝐛𝐬_𝐭𝐨𝐥
Vous pouvez préciser la différence tolérable avec les arguments 𝐫𝐞𝐥_𝐭𝐨𝐥 et 𝐚𝐛𝐬_𝐭𝐨𝐥 . Par défaut, 𝐫𝐞𝐥_𝐭𝐨𝐥 est défini sur 1𝐞-9 et 𝐚𝐛𝐬_𝐭𝐨𝐥 est défini sur 0.0 .
𝐫𝐞𝐥_𝐭𝐨𝐥 est la tolérance relative, et c’est la différence maximale autorisée entre deux valeurs, par rapport à la valeur absolue la plus grande.
abs(a - b) <= rel_tol * max(abs(a), abs(b))
print(math.isclose(1, 1.001)) # False print(math.isclose(1, 1.001, rel_tol=0.01)) # True
𝐚𝐛𝐬_𝐭𝐨𝐥 est la tolérance absolue minimale.
abs(a - b) <= abs_tol
Elle est comparée à la plus grande des tolérances relatives et absolues.
abs(a - b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)
Soyez prudent lorsque vous comparez un nombre avec 0 . Par exemple, si 𝐛=0 , la comparaison 𝐦𝐚𝐭𝐡.𝐢𝐬𝐜𝐥𝐨𝐬𝐞(𝐚, 𝐛) ne renverra pas T𝐫𝐮𝐞 à moins que 𝐫𝐞𝐥_𝐭𝐨𝐥=0 . Cela est dû au fait que la comparaison est réalisée comme suit :
abs(a) <= rel_tol * abs(a)
N’oubliez pas de précision 𝐚𝐛𝐬_𝐭𝐨𝐥 lorsque cela est nécessaire, car la valeur par défaut est 0,0 .
print(math.isclose(0, 0.001)) # False print(math.isclose(0, 0.001, rel_tol=0.01)) # False print(math.isclose(0, 0.001, abs_tol=0.01)) # True
Comparer un nombre à virgule flottante avec 0
Comme mentionné ci-dessus, soyez prudent lorsque vous comparez le résultat d’une opération à virgule flottante à 0. L’utilisation de == ou 𝐦𝐚𝐭𝐡.𝐢𝐬𝐜𝐥𝐨𝐬𝐞() par défaut peut produire des résultats inattendus.
Il en va de même pour les opérations portant sur des nombres irrationnels, tels que pi.
print(math.sin(math.pi)) # 1.2246467991473532e-16 print(math.sin(math.pi) == 0) # False print(math.isclose(math.sin(math.pi), 0)) # False
Vous pouvez définir 𝐚𝐛𝐬_𝐭𝐨𝐥 dans 𝐦𝐚𝐭𝐡.𝐢𝐬𝐜𝐥𝐨𝐬𝐞() , arrondir avec 𝐫𝐨𝐮𝐧𝐝() , ou comparer avec une valeur suffisamment petite au lieu de 0 .
print(math.isclose(math.sin(math.pi), 0, abs_tol=1e-10)) # True print(round(math.sin(math.pi), 10) == 0) # True print(abs(math.sin(math.pi)) < 1e-10) # True
