Question 1Quel est le choix le plus approprié quand tu ne peux pas tolérer même un décalage d'un centime dans des calculs monétaires ?
decimal et fractions — Arithmétique précise sans erreurs de float
Apprends les modules decimal et fractions de Python depuis la base. Couvre le problème 0.1 + 0.2 != 0.3 avec float, le calcul monétaire exact avec Decimal (et pourquoi tu construis Decimal à partir de chaînes), l'arithmétique avec Fraction auto-réduit, et le choix parmi les trois types numériques — avec des exercices pratiques exécutables.
Cet article couvre les deux modules pour les cas où `float` cause des problèmes. decimal.Decimal fait de l'arithmétique exacte en base 10, ce qui le rend essentiel pour les calculs monétaires ; fractions.Fraction garde le numérateur et le dénominateur sous forme d'entiers pour calculer avec des fractions réduites de façon exacte. Les deux existent pour éliminer les erreurs d'arrondi inévitables de float.
Erreurs d'arrondi de float et decimal.Decimal — N'utilise pas float pour des calculs précis
Le float de Python est représenté en interne en binaire, donc les fractions décimales comme `0.1` qui ne se divisent pas proprement en binaire portent une petite erreur d'arrondi. L'exemple le plus célèbre est `0.1 + 0.2 == 0.3` qui retourne `False`. Pour le graphique ou le calcul scientifique, l'erreur est négligeable, mais dans les sommes financières même un petit décalage devient critique.
C'est pour ça que decimal.Decimal existe. Il est représenté en interne en base 10, donc construire un Decimal depuis la chaîne "0.1" te permet de calculer sans erreur.
0.1. Decimal utilise une représentation interne en base 10, donc il calcule exactement comme la chaîne d'entrée le dit, mais est plus lent que float. Pour le traitement de l'argent et des taxes, choisis Decimal.from decimal import Decimal
# erreur d'arrondi de float
print(0.1 + 0.2) # 0.30000000000000004
print(0.1 + 0.2 == 0.3) # False ← contre-intuitif !
# Construis Decimal depuis une chaîne (depuis un float l'erreur est héritée)
a = Decimal("0.1")
b = Decimal("0.2")
print(a + b) # 0.3
print(a + b == Decimal("0.3")) # True
# Un exemple monétaire
price = Decimal("1980")
tax_rate = Decimal("0.10")
total = price * (Decimal("1") + tax_rate)
print(total) # 2178.00
Construis Decimal depuis une chaîne, pas depuis un float
Si tu passes un float — Decimal(0.1) — tu transmets une valeur qui porte déjà une erreur d'arrondi, donc tu finis avec un Decimal qui contient toujours l'erreur du float. Passe toujours une chaîne : Decimal("0.1"). C'est difficile à repérer dans les tests et c'est le piège classique qui produit pour la première fois en production un décalage d'un centime.
fractions.Fraction — Garder les fractions comme fractions
Fraction est un type qui stocke le numérateur et le dénominateur sous forme d'entiers pour l'arithmétique. Construis-en un avec Fraction(1, 3), et les additions, soustractions et multiplications suivantes retournent un résultat de fraction réduite. En float, 1/3 est l'approximation 0.3333333333333333, mais avec Fraction il reste "un tiers" lui-même.
+ / - / * sont aussi des Fraction, et ils sont automatiquement réduits. Tu peux convertir en float avec float(f), mais ça introduit une erreur d'arrondi.| Type | Représentation interne | Meilleur usage |
|---|---|---|
| int | Entier | Comptes et décomptes (sans erreur) |
| float | Virgule flottante binaire | Calcul scientifique, graphique (rapide, avec erreur) |
| Decimal | Représentation en base 10 | Argent et taxes (quand la précision base 10 compte) |
| Fraction | Numérateur et dénominateur (entiers) | Ratios et probabilités (quand tu veux une forme réduite) |
Les objets Fraction exposent des attributs pour le numérateur et le dénominateur. f.numerator retourne le numérateur et f.denominator retourne le dénominateur — les deux après réduction. Par exemple, Fraction(2, 6) est normalisé en interne en Fraction(1, 3), donc .numerator est 1 et .denominator est 3.
| Attribut / Méthode | Sens | Exemple |
|---|---|---|
| f.numerator | Numérateur | Fraction(1, 3).numerator → 1 |
| f.denominator | Dénominateur | Fraction(1, 3).denominator → 3 |
| float(f) | Convertir en float | float(Fraction(1, 2)) → 0.5 |
Vérification des connaissances
Répondez à chaque question une par une.
Question 2En construisant un Decimal, quelle est la bonne façon qui n'introduit pas d'erreur ?
Question 3Lequel choisis-tu quand tu veux calculer des ratios ou probabilités sous forme de fractions réduites ?