Annulation catastrophique

Cette leçon aborde l'un des pièges les plus dangereux de l'arithmétique flottante : la perte massive de chiffres significatifs lors de la soustraction de deux nombres proches. Ce phénomène, appelé annulation catastrophique (ou cancellation en anglais), peut transformer un calcul apparemment anodin en résultat complètement faux.

Le problème en une phrase

Quand on soustrait deux nombres presque égaux, les chiffres significatifs de tête s'annulent, et il ne reste que les chiffres de queue — qui sont souvent entachés d'erreurs.

Un exemple qui fait peur

Imaginons que vous mesuriez deux longueurs très précisément :

Vous voulez calculer la différence .

Calcul direct :

Tout semble correct. Mais combien de CSE possède ce résultat ?

Analyse détaillée de la perte de précision

Représentation en notation scientifique

Écrivons les valeurs en notation scientifique normalisée :

La soustraction

Renormalisation

Le résultat n'est pas normalisé. On doit décaler :

Le problème révélé

Regardons ce qui s'est passé visuellement :

text
U* = 0,83672 × 10²
- V* = 0,83666 × 10²
─────────────────────
       0,00006 × 10²
     = 0,6     × 10⁻²

Les 4 premiers chiffres (8, 3, 6, 6) se sont annulés !
Il ne reste qu'UN SEUL chiffre : le 6.

Nous avions 5 CSE dans chaque opérande, mais le résultat n'en a plus qu'un seul !

Pourquoi cela arrive-t-il ?

L'explication intuitive

Les chiffres de tête de et sont identiques : 8, 3, 6, 6. Ces chiffres représentent l'information « commune » aux deux nombres. Quand on soustrait, cette information commune disparaît.

Ce qui reste, ce sont les chiffres où et diffèrent. Mais ces chiffres de queue sont justement ceux qui portent le plus d'incertitude !

L'explication avec les erreurs

Supposons que les vraies valeurs soient :

Nos mesures et ont chacune une erreur d'environ .

Calcul exact :

Calcul avec nos approximations :

Erreur sur le résultat :

Erreur relative sur le résultat :

Nous sommes passés d'une erreur relative de sur les opérandes à une erreur de 8% sur le résultat !

🚨

Règle fondamentale

Lors d'une soustraction de nombres proches, les erreurs relatives s'amplifient considérablement. Plus les nombres sont proches, plus l'amplification est dramatique.

Démonstration par intervalles de confiance

Une façon particulièrement visuelle de comprendre l'annulation catastrophique est de raisonner en termes d'intervalles de confiance.

Le principe

Quand une valeur est connue avec une incertitude, on peut l'écrire sous la forme :

L'intervalle représente toutes les valeurs possibles de .

Exemple numérique détaillé

Prenons deux mesures :

Erreurs relatives initiales :

Les deux grandeurs sont connues avec une excellente précision de 1%.

Calcul de la différence

Calculons .

Valeur centrale :

Intervalle de confiance pour z :

Valeur minimale :

Valeur maximale :

Donc :

L'explosion de l'erreur relative

Erreur absolue sur z :

Erreur relative sur z :

L'erreur relative a été multipliée par 100 en une seule soustraction !

La formule générale

Pour une soustraction , l'erreur relative sur le résultat est :

Cette formule révèle le mécanisme de l'annulation :

  • Au numérateur : les erreurs absolues s'additionnent (elles ne se compensent pas)
  • Au dénominateur : quand , la différence est petite

Le rapport explose quand le dénominateur tend vers zéro !

🚨

Loi de l'amplification

Si et ont la même erreur relative et sont proches (), alors :

Plus et sont proches, plus le facteur d'amplification est grand.

Autre exemple : des nombres très proches

Considérons :

Différence :

Erreur relative :

L'erreur relative de 0,1% est devenue 200% — le résultat est totalement non significatif !

intervalles_confiance.pypython
def soustraction_avec_incertitude(x, dx, y, dy):
  """
  Calcule z = x - y avec propagation des incertitudes.

  Paramètres :
      x, dx : valeur et incertitude absolue de x
      y, dy : valeur et incertitude absolue de y

  Retourne :
      z, dz, epsilon_z : résultat, incertitude absolue, erreur relative
  """
  z = x - y
  dz = dx + dy  # Les erreurs s'additionnent pour une soustraction
  epsilon_z = dz / abs(z) if z != 0 else float('inf')
  return z, dz, epsilon_z

# Exemple 1 : x = 102 ± 1, y = 100 ± 1
print("=== Exemple 1 : nombres assez proches ===")
x, dx = 102, 1
y, dy = 100, 1
z, dz, eps = soustraction_avec_incertitude(x, dx, y, dy)
print(f"x = {x} ± {dx}  (erreur relative : {dx/x*100:.1f}%)")
print(f"y = {y} ± {dy}  (erreur relative : {dy/y*100:.1f}%)")
print(f"x - y = {z} ± {dz}  (erreur relative : {eps*100:.1f}%)")
print(f"Facteur d'amplification : ×{eps / (dx/x):.0f}")

print()

# Exemple 2 : x = 1000 ± 1, y = 999 ± 1
print("=== Exemple 2 : nombres très proches ===")
x, dx = 1000, 1
y, dy = 999, 1
z, dz, eps = soustraction_avec_incertitude(x, dx, y, dy)
print(f"x = {x} ± {dx}  (erreur relative : {dx/x*100:.2f}%)")
print(f"y = {y} ± {dy}  (erreur relative : {dy/y*100:.2f}%)")
print(f"x - y = {z} ± {dz}  (erreur relative : {eps*100:.0f}%)")
print(f"Facteur d'amplification : ×{eps / (dx/x):.0f}")

Quantifier la perte de CSE

Formule approximative

Quand on soustrait deux nombres et ayant CSE chacun, le résultat perd environ :

Application à notre exemple

On perd environ 4 CSE. Partant de 5 CSE, il en reste environ 1. C'est cohérent avec notre analyse !

Tableau de la catastrophe

Rapport U/|U-V|CSE perdusSituation
101Tolérable
1002Préoccupant
1 0003Problématique
10 0004Grave
1 000 0006Catastrophique

Démonstration en Python

annulation_catastrophique.pypython
import math

# Exemple 1 : Calcul de √(x+1) - √x pour grand x
def difference_racines_naive(x):
  return math.sqrt(x + 1) - math.sqrt(x)

# Testons pour des valeurs croissantes de x
print("=== Annulation catastrophique avec √(x+1) - √x ===
")
print(f"{'x':>15} | {'Résultat naïf':>20} | {'Résultat exact':>20}")
print("-" * 60)

for exp in range(1, 16):
  x = 10 ** exp
  naif = difference_racines_naive(x)
  # Valeur "exacte" par développement limité : 1/(2√x) pour grand x
  exact = 1 / (2 * math.sqrt(x))
  print(f"{x:>15.0e} | {naif:>20.15f} | {exact:>20.15f}")

print("
Observez : pour x = 10¹⁵, le résultat naïf devient 0 !")

Ce code montre que pour de grandes valeurs de , le calcul naïf de donne des résultats de plus en plus faux, jusqu'à retourner exactement 0 — alors que la vraie valeur n'est jamais nulle !

Stratégies d'évitement

Heureusement, il existe des techniques pour contourner l'annulation catastrophique.

Stratégie 1 : Reformulation algébrique

L'idée est de réécrire l'expression mathématique sous une forme équivalente qui évite la soustraction problématique. Voyons un exemple détaillé avec la technique de multiplication par le conjugué.

Exemple : Les racines carrées

Problème : Calculer pour grand .

Forme naïve : Soustraction directe → annulation catastrophique.

Reformulation : On utilise la technique de multiplication par le conjugué.

Qu'est-ce que le conjugué ?

Le conjugué d'une expression de la forme est l'expression (on change le signe du milieu).

ExpressionSon conjugué

Pourquoi c'est utile ?

Quand on multiplie une expression par son conjugué, on obtient une différence de carrés :

C'est l'identité remarquable que vous connaissez depuis le secondaire !

L'intérêt : si et contiennent des racines carrées, celles-ci disparaissent quand on les met au carré.

Exemple simple :

Les racines ont disparu ! Le résultat est un nombre entier.

L'astuce : multiplier ET diviser

Évidemment, on ne peut pas juste multiplier notre expression par son conjugué — cela changerait sa valeur. L'astuce est de multiplier et diviser par la même chose (ce qui revient à multiplier par 1) :

Application pas à pas

Étape 1 : Identifier le conjugué de .

Le conjugué est .

Étape 2 : Multiplier le numérateur (en utilisant ).

Le numérateur se simplifie en... 1 ! Les racines ont disparu.

Étape 3 : Écrire le résultat final.

Pourquoi cette forme est-elle stable ?

Comparons les deux formes :

FormeOpération dangereuse ?Pour x = 10⁸
Oui : soustraction de nombres proches
Non : division simple

La nouvelle forme ne contient aucune soustraction de nombres proches ! On divise 1 par une somme de deux grands nombres, ce qui est numériquement stable.

racines_reformule.pypython
import math

def difference_racines_naive(x):
  return math.sqrt(x + 1) - math.sqrt(x)

def difference_racines_stable(x):
  # Forme reformulée : 1 / (√(x+1) + √x)
  return 1 / (math.sqrt(x + 1) + math.sqrt(x))

print("=== Comparaison des deux méthodes ===
")
print(f"{'x':>12} | {'Naïf':>18} | {'Stable':>18} | {'Erreur naïf':>12}")
print("-" * 70)

for exp in range(1, 16):
  x = 10 ** exp
  naif = difference_racines_naive(x)
  stable = difference_racines_stable(x)
  erreur = abs(naif - stable) / stable * 100 if stable != 0 else float('inf')
  print(f"{x:>12.0e} | {naif:>18.15f} | {stable:>18.15f} | {erreur:>10.2f} %")

Stratégie 2 : Utiliser des séries de Taylor

Quand est petit, on peut remplacer une expression par son développement en série.

Exemple 1 : Calculer pour proche de 0.

Problème : Quand , , donc est une soustraction de nombres proches.

Solution : Utiliser le développement de Taylor :

Donc :

Cette formule ne contient aucune soustraction de nombres proches !

Exemple 2 : Calculer pour proche de 0.

Problème : Quand , on calcule , puis on soustrait 1. Les 10 premiers chiffres s'annulent !

Solution : Utiliser le développement de Taylor :

Donc :

Python fournit directement la fonction math.expm1(x) qui implémente ce calcul stable :

expm1_exemple.pypython
import math

x = 1e-15

# Forme naïve : annulation catastrophique
naif = math.exp(x) - 1
print(f"exp({x}) - 1 (naïf)  = {naif}")

# Forme stable : fonction dédiée
stable = math.expm1(x)
print(f"expm1({x}) (stable) = {stable}")

# Erreur relative
erreur = abs(naif - stable) / stable * 100
print(f"Erreur relative du calcul naïf : {erreur:.1f}%")

Stratégie 3 : Changer l'ordre des opérations

Parfois, réorganiser l'ordre des calculs peut aider.

Exemple : Calculer la somme où les sont de signes alternés.

Mauvaise approche : Additionner dans l'ordre, ce qui crée des sommes partielles qui oscillent.

Meilleure approche : Séparer les termes positifs et négatifs, sommer chaque groupe, puis faire une seule soustraction finale.

Stratégie 4 : Augmenter la précision temporairement

Si aucune reformulation n'est possible, on peut effectuer le calcul critique en précision supérieure.

precision_etendue.pypython
from decimal import Decimal, getcontext

# Augmenter la précision à 50 chiffres
getcontext().prec = 50

a = Decimal('83.672')
b = Decimal('83.666')

# Soustraction en haute précision
resultat = a - b
print(f"Résultat en haute précision : {resultat}")

# Convertir en float standard pour la suite des calculs
resultat_float = float(resultat)
print(f"Reconverti en float : {resultat_float}")

Récapitulatif : Reconnaître et éviter l'annulation

Signal d'alarmeAction recommandée
Soustraction de nombres du même ordre de grandeurChercher une reformulation algébrique
Expression avec petit Utiliser un développement de Taylor
Calcul de Multiplier/diviser par
Résultat proche de zéro issu de grands nombresAugmenter la précision ou reformuler
⚠️

À retenir

L'annulation catastrophique ne produit pas de message d'erreur. Le calcul s'exécute normalement et retourne un résultat — qui peut être complètement faux. C'est au programmeur d'anticiper ce risque et de choisir des formulations numériquement stables.

Ce qu'il faut retenir

L'annulation catastrophique survient lors de la soustraction de deux nombres proches. Les chiffres significatifs de tête s'annulent, ne laissant que les chiffres de queue — souvent contaminés par les erreurs d'arrondi.

La parade principale est la reformulation algébrique : trouver une expression mathématiquement équivalente mais numériquement stable. Les identités remarquables, les développements de Taylor et la multiplication par le conjugué sont des outils précieux.

Dans la prochaine leçon, nous verrons comment ces erreurs se propagent à travers les calculs, en utilisant les séries de Taylor pour analyser la propagation d'erreurs dans les fonctions.

Exercices de réflexion

Exercice 1 : Détecter le danger

Pour chacune des expressions suivantes, indiquez si elle présente un risque d'annulation catastrophique et dans quelles conditions :

  1. quand
  2. quand
  3. quand
  4. quand est grand

Exercice 2 : Reformuler

Proposez une formulation stable pour calculer :

Indice : Utilisez l'identité et l'identité .

Exercice 3 : Expérimentation

Écrivez un programme Python qui :

  1. Calcule pour
  2. Compare avec la valeur attendue
  3. Explique les résultats observés

À partir de quelle valeur de le résultat devient-il complètement faux ?