Semaine 3 · Jour 2 · 420-SN1

LES NOMBRES RÉELS
(et leurs pièges)

Calculer juste en sciences : quand l'ordinateur arrondit dans ton dos 🚀

Le choc : 0.1 + 0.2

print(0.1 + 0.2) # 0.30000000000000004 (!?) print(0.1 + 0.2 == 0.3) # False

Ce n'est pas un bogue de Python : c'est une réalité de tous les ordinateurs. Les nombres réels sont rangés de façon approximative, et ces minuscules écarts ont de vraies conséquences en calcul scientifique. Voyons pourquoi - et comment s'en protéger.

Pourquoi ? Le binaire a une fin

Pour nous, en décimal
1 / 3 = 0.33333... # jamais fini !
Pour l'ordi, en binaire
0.1 = 0.0001100110011... # jamais fini non plus

Un ordinateur n'a qu'un nombre fini de bits (64) pour ranger un réel. Comme 1/3 qu'on ne peut pas écrire exactement en décimal, beaucoup de nombres - même 0,1 ! - sont infinis en binaire. L'ordinateur les coupe et garde une valeur très proche, mais pas exacte.

L'intervalle de validité

TypeDe ... à ...Précision / piège
int (Python)illimité 🎉grandit tout seul (big int natif)
float (Python)jusqu'à ±1.8e308~15-16 chiffres ; au-delà → inf
float (minuscule)jusqu'à 5e-324en dessous → 0.0 (disparaît)
numpy int64±9.2e18⚠ déborde en silence

Bonne nouvelle : en Python pur, les entiers sont illimités. Mais le float a des bornes, et dès qu'on passe à numpy (omniprésent en sciences), les entiers redeviennent bornés et peuvent déborder sans prévenir. Toujours savoir dans quel intervalle on travaille.

numpy : .sum() garde le type, .mean() donne un float

Σ .sum() → même type
import numpy as np ages = np.array([20, 21, 19]) ages.sum() # 60 type(ages.sum()) # int64 (entier)
μ .mean() → toujours float
ages = np.array([20, 21, 19]) ages.mean() # 20.0 type(ages.mean()) # float64

Sur un tableau numpy, .mean() fait une vraie division et renvoie toujours un float. Mais .sum() conserve le type du tableau : des entiers donnent un entier (un int64 qui peut déborder, cf. slide précédente), des flottants donnent un flottant. Pour une grosse somme d'entiers sûre, on convertit d'abord (ages.astype(float)) ou on reste en int Python.

Ne jamais comparer avec ==

❌ Fragile
a = 0.1 + 0.2 if a == 0.3: print("egal") # ne s'affiche jamais
✅ Robuste
import math a = 0.1 + 0.2 if math.isclose(a, 0.3): print("assez proche")

En sciences, deux réels sont « égaux » s'ils sont assez proches. math.isclose(a, b) compare avec une tolérance ; sinon on teste soi-même abs(a - b) < 1e-9. Le == strict reste réservé aux entiers.

L'erreur qui s'accumule

total = 0.0 for pas in range(10): total = total + 0.1 print(total) # 0.9999999999999999 print(total == 1.0) # False import math print(math.fsum([0.1] * 10)) # 1.0 exact
pas dérive vraie calculée

Chaque opération ajoute une miette d'erreur. Dans une boucle de simulation (une trajectoire calculée pas à pas, comme la chute de S3.1), ces miettes s'additionnent et le résultat dérive. math.fsum() fait une somme compensée bien plus précise qu'une addition naïve.

Soustraire deux nombres proches

grand = 1e16 grandPlusUn = grand + 1 print(grandPlusUn - grand) # 0.0 -> le + 1 a complètement disparu !

À très grande échelle, l'écart entre deux réels voisins dépasse 1 : ajouter 1 à 1e16 ne change rien. Soustraire ensuite deux nombres presque égaux ne laisse que de l'erreur - on appelle ça la cancellation catastrophique. En astronomie (différence de deux grandes distances), on réorganise la formule pour l'éviter.

inf et nan : les valeurs fantômes

print(1e308 * 10) # inf (dépassement) print(float("inf") - float("inf")) # nan (indéfini) resultat = float("nan") print(resultat == resultat) # False (!) nan n'égale rien print(math.isnan(resultat)) # True -> la bonne façon de tester

inf apparaît quand un résultat dépasse la borne ; nan (« not a number ») quand il est indéfini (inf - inf, 0/0). Piège célèbre : nan n'est égal à rien, pas même à lui-même. On les détecte avec math.isnan() et math.isinf().

Arrondir... à l'affichage seulement

vitesse = 29.7853333 # km/s mesurés # Pour AFFICHER : print(round(vitesse, 2)) # 29.79 print(f"{vitesse:.2f} km/s") # 29.79 km/s # Mais on continue les CALCULS avec la valeur complète !

On arrondit pour montrer un résultat, jamais au milieu d'un calcul : arrondir trop tôt jette de la précision qu'on ne récupère plus. round() renvoie un nombre, f"{x:.2f}" renvoie un texte mis en forme. Garde toujours la valeur complète pour les étapes suivantes.

Quand float ne suffit pas

Decimal · base 10 exacte
from decimal import Decimal a = Decimal("0.1") + Decimal("0.2") print(a) # 0.3 exact
Fraction · rationnels exacts
from fractions import Fraction tiers = Fraction(1, 3) print(tiers + tiers + tiers) # 1

Decimal calcule en base 10 comme nous (idéal pour l'argent ou des mesures à décimales exactes) ; Fraction garde les nombres sous forme de fractions exactes (1/3 reste 1/3, zéro erreur). Plus lents que float, mais exacts.

PI à 50 décimales : mpmath

import math print(math.pi) # 3.141592653589793 (~15 décimales : le maximum du float) from mpmath import mp mp.dps = 50 # dps = nombre de chiffres voulus print(mp.pi) # 3.1415926535897932384626433832795028841971693993751

Le float plafonne PI à ~15 décimales. Pour aller plus loin (les « big réels »), mpmath calcule avec autant de chiffres qu'on veut : on règle mp.dps et on obtient PI à 50, 100, 1000 décimales. L'outil du calcul scientifique de haute précision.

Les très grands entiers

✅ Python pur
print(2 ** 100) # 1267650600228229401496703205376 # aucun débordement, jamais
❌ numpy int64
import numpy as np print(np.int64(2) ** 63) # nombre négatif faux ! (débordement)

Pas besoin de module pour les big int en Python : un entier grandit indéfiniment (factorielles géantes, cryptographie...). Le danger vient de numpy, dont les int64 ont une borne : pour de très grands comptes, on reste en entier Python (ou dtype=object).

La boîte à outils du calcul fiable

Le piègeLe réflexe
Comparer deux réelsmath.isclose(a, b)
Sommer beaucoup de réelsmath.fsum(liste)
Afficher un résultatround(x, 2) / f"{x:.2f}"
Argent, décimales exactesDecimal / Fraction
Très haute précision (PI...)mpmath (mp.dps)
Très grands entiersint Python (pas numpy)

Le réel parfait n'existe pas dans la machine - mais avec ces réflexes, tes calculs restent justes et honnêtes. Le fond technique complet (IEEE 754, epsilon machine) t'attend dans l'En savoir plus.

À TON CARNET ! 🛰

Tape 0.1 + 0.2, observe le résultat, puis rends la comparaison juste avec math.isclose
⤺ Refermer le diaporama