L'addition des flottants n'est pas associative
Exercice de TD · niveau 3 (difficile) · NSI (première), chapitre 2 — Flottants, booléens et textes · Écriture binaire et pièges des flottants
Énoncé
L'addition des flottants n'est pas associative. (a) Comparer (1.0 + 1e16) - 1e16 et 1.0 + (1e16 - 1e16). (b) Expliquer le phénomène d'absorption : pourquoi 1e16 + 1.0 == 1e16 ? (c) On veut additionner dix fois puis ajouter . L'ordre change-t-il le résultat ? Conclure sur l'ordre de sommation.
Corrigé
(a) Les deux expressions valent respectivement et :
>>> (1.0 + 1e16) - 1e16
0.0
>>> 1.0 + (1e16 - 1e16)
1.0
Même somme mathématique, deux résultats. L'associativité, vraie sur , est fausse sur les flottants.
(b) L'absorption. Un flottant garde bits de mantisse. Au voisinage de , deux flottants consécutifs sont distants de (car , donc vit entre et , où le pas vaut ). Ajouter , c'est demander un nombre situé entre deux flottants voisins : l'arrondi le ramène au plus proche, qui est le point de départ. Le petit nombre est absorbé — il disparaît sans erreur ni avertissement.
(c) Oui, l'ordre change tout :
>>> s = 1e16
>>> for _ in range(10):
... s = s + 1.0
>>> s
1e+16
>>> s = 0.0
>>> for _ in range(10):
... s = s + 1.0
>>> s + 1e16
1.000000000000001e+16
Dans le premier ordre, les dix unités sont absorbées une par une : on en perd dix. Dans le second, elles s'accumulent d'abord jusqu'à — qui, lui, n'est plus négligeable devant le pas de — puis la somme est faite une seule fois.
Conclusion pratique. Pour sommer beaucoup de termes, additionner les plus petits d'abord (trier par valeur absolue croissante), ou utiliser math.fsum, qui garantit le résultat correctement arrondi.
Contrôle : math.fsum([0.1] * 1000) rend exactement 100.0, là où la boucle naïve rend 99.9999999999986 — une erreur de accumulée en mille additions.
Les autres exercices de ce chapitre Le cours du chapitre
Un blocage sur cet exercice ? Le tuteur d'Adloun guide par questions, sans donner la réponse.