Aller au contenu

Des calculs déroutants

Au problème du nombre fini de chiffres s'ajoute le problème de la représentation des nombres décimaux en base 2. Cela donne des résultats dont il faut apprendre à se méfier...

Exemple

  1. Tout d'abord, calculons 0.1 + 0.1 :

    >>> 0.1 + 0.1
    0.2
    
    >>> 0.1 + 0.1 == 0.2
    True
    
    En demandant à Python, si, pour lui, 0.1 + 0.1 est bien égal à 0.2, tout semble bien se passer.

  2. A présent, calculons 0.1 + 0.1 + 0.1 :

    >>> 0.1 + 0.1 + 0.1
    0.30000000000000004
    
    Étonnant, il semble que le résultat ne soit pas 0.3 !

  3. Demandons alors à Python si 0.1 + 0.1 + 0.1 est bien égal à 0.3 :

    >>> 0.1 + 0.1 + 0.1 == 0.3
    False
    
    Étonnant n'est-ce pas ?

  4. De même :

    >>> 0.1 + 0.2 == 0.3
    False
    
    Python répond FAUX !

Important

Cela n'est pas lié au langage Python. On retrouvera ce problème dans tous les langages de programmation.
Ce problème est dû à la façon dont les nombres à virgule sont codés en machine.

A retenir

On ne doit jamais tester l'égalité de deux flottants avec le comparateur « == ».
Il faudra se rappeler de l'exemple du calcul de 0.1 + 0.1 + 0.1 ci-dessus.

Si l'on cherche à tester l'égalité de flottants en machine, on ne teste que leur proximité (avec risques d'erreur donc).
Le module math de Python propose une fonction spécifique pour ce type de test : isclose().

>>> from math import isclose
>>> isclose(0.1 + 0.2, 0.3)
True

Exemple

La fonction fractions_egales() a pour paramètres les entiers a, b, c et d. Cette fonction renvoie True si la fraction \frac{a}{b} est égale à la fraction \frac{c}{d} et False sinon.

  1. Quelle précondition sur les paramètres semble-t-il raisonnable d'imposer ?

    Une réponse

    Les paramètres b et d, qui représentent les dénominateurs, doivent être non nuls.

  2. Complétez le code de la fonction en ajoutant une assertion pour traduire la précondition précédente.

    1
    2
    3
    4
    5
    6
    def fractions_egales(a, b, c, d):
        """
        a, b, c, d – int, entiers avec b et d non nuls
        Sortie: bool – True lorsque la fraction a/b est égale à c/d,
                       False sinon
        """
    
    Attention

    Le test if a/b == c/d serait une bien mauvaise idée.
    Rappelez-vous : on ne peut pas vérifier l'égalité de deux flottants avec « == » !

    Une solution
    1
    2
    3
    4
    5
    6
    7
    8
    def fractions_egales(a, b, c, d):
        """
        a, b, c, d – int, entiers avec b et d non nuls
        Sortie: bool – True lorsque  est égale à ,
                       False sinon
        """
        assert (b!= 0) and (d!= 0), "dénominateur nul !"
        return a*d == b*c
    

Des règles de calcul étonnantes

Info

Soient a, b et c des nombres réels. On a : (a+b)+c = a+(b+c).

C'est ce que l'on appelle associativité de l'addition de nombres réels (le mot « associativité » est utilisé pour rappeler que l'on peut, pour additionner, « associer » les deux premiers ou « associer » les deux derniers).

En machine, l'addition des flottants n'est pas associative :

>>> (0.3 + 0.9) + 0.2
1.4

>>> 0.3 + (0.9 + 0.2)
1.4000000000000001

>>> (0.3 + 0.9) + 0.2 == 0.3 + (0.9 + 0.2)
False

L'essentiel des calculs en machine sur les flottants ne sont donc que des calculs de valeurs approchées.

On insiste donc, une fois de plus, sur la non-utilisation de « == » entre flottants : l'information obtenue avec un test a == b (a et b sont de type float) ne concerne que les flottants et donne rarement une information sur les nombres mathématiques qu'ils sont censés représenter.