1. Introduction
Il peut sembler que si l’ordinateur est « intelligent », alors 0.1 + 0.2 devrait tout simplement donner 0.3. Mais ce n’est pas tout à fait le cas. Voyons cela avec un exemple simple.
double x = 0.1;
double y = 0.2;
double sum = x + y;
System.out.println(sum); // Que va afficher le programme ?
Et maintenant, essayez de comparer à 0.3 :
System.out.println(sum == 0.3); // Et ici, sera-ce true ou false ?
Si vous voyez false, ne soyez pas surpris !
La raison se cache dans la représentation des nombres en mémoire
Les ordinateurs travaillent en base binaire. Or, loin de toutes les fractions décimales peuvent être représentées comme des fractions binaires finies, tout comme 1/3 ne peut pas être écrit exactement en décimal (0.333...). Par exemple, 0.1 en binaire est une fraction infinie, et il faut l’« arrondir » pour la stocker.
En termes simples, le type double fait parfois semblant de stocker votre nombre exactement, mais en réalité il ne conserve qu’une valeur approchée très proche.
2. Quelles « bizarreries » apparaissent lors de l’arithmétique avec double ?
Voyons des exemples concrets.
Exemple 1. La « magie » classique 0.1 + 0.2
double a = 0.1;
double b = 0.2;
double sum = a + b;
System.out.println(sum); // 0.30000000000000004
System.out.println(sum == 0.3); // false
L’ordinateur n’affiche pas 0.3, mais 0.30000000000000004. La différence est minime, mais si vous faites, par exemple, de la finance — c’est déjà critique.
Exemple 2. Addition itérative
double result = 0;
for (int i = 0; i < 10; i++)
{
result += 0.1;
}
System.out.println(result); // 0.9999999999999999
Vous vouliez 1.0 — vous avez obtenu un peu moins. Là encore, la raison est l’arrondi interne de double.
Pourquoi est-ce important dans des tâches réelles
Beaucoup pensent : « Quelle importance, une petite erreur, et alors ! » Examinons un exemple du monde des paiements.
Supposons que votre banque en ligne additionne 100 transactions de 0.1 €. Si votre programme « perd » un cent‑millième d’euro à chaque itération, à l’échelle d’une banque vous allez déjà « perdre » de l’argent réel. Là, le comptable viendra immédiatement vous voir et demandera : « Où est passé notre argent ?! »
3. Comment comparer correctement les nombres à virgule flottante
Puisque le type double ne peut souvent pas stocker exactement la valeur attendue, une comparaison directe avec == peut vous piéger. À la place, on compare le module de la différence à un très petit nombre (epsilon).
Exemple de comparaison avec une tolérance
double a = 0.1 + 0.2;
double b = 0.3;
double epsilon = 0.000001;
if (Math.abs(a - b) < epsilon)
{
System.out.println("Presque égal !"); // C’est plus sûr de comparer ainsi
}
Ici, nous disons : « Si la différence entre les nombres est inférieure à un millionième, nous considérons que les nombres sont égaux. »
Remarque : la fonction Math.abs(value) retourne la valeur absolue (module) du nombre passé.
4. Valeurs spéciales de double : Infinity, NaN, -Infinity
Le type double ne stocke pas seulement des nombres, mais aussi des valeurs particulières. Elles apparaissent dans des situations qu’un mathématicien interdit strictement à un étudiant.
Infini (Infinity)
Que se passe‑t‑il si l’on divise 1 par 0 ?
double result = 1.0 / 0.0;
System.out.println(result); // Infinity
En Java (et dans beaucoup d’autres langages), la division par 0 pour un double ne lève pas d’exception ! À la place, le résultat devient une valeur spéciale « infini positif ».
Moins l’infini (-Infinity)
Si vous divisez un nombre négatif par 0, vous obtenez l’infini négatif :
double result = -1.0 / 0.0;
System.out.println(result); // -Infinity
« Pas un nombre » (NaN — Not a Number)
Si vous faites quelque chose de vraiment étrange, par exemple tenter de calculer la racine carrée d’un nombre négatif :
double result = Math.sqrt(-1);
System.out.println(result); // NaN
Ou le résultat de la division 0.0 / 0.0 :
double result = 0.0 / 0.0;
System.out.println(result); // NaN
NaN correspond à « tout ce qui ne serait pas un nombre dans la vraie vie ».
Vérification des valeurs spéciales
Java propose des fonctions pour vérifier ces valeurs :
System.out.println(Double.isInfinite(result)); // true, si infini
System.out.println(Double.isNaN(result)); // true, si NaN
Tableau : comment double réagit aux opérations inhabituelles
| Opération | Résultat | Ce qui est stocké dans double |
|---|---|---|
|
Infinity | +∞ |
|
-Infinity | -∞ |
|
NaN | Pas un nombre |
|
NaN | Pas un nombre |
5. Erreurs typiques lors du travail avec les nombres à virgule flottante
Erreur n° 1 : comparer des nombres décimaux avec ==
Le piège le plus courant : essayer de vérifier l’égalité de deux nombres à virgule flottante calculés via une comparaison directe. À cause de l’accumulation des erreurs d’arrondi, vous obtiendrez presque toujours un résultat inattendu. Utilisez toujours une comparaison avec tolérance (epsilon).
Erreur n° 2 : NaN et Infinity inattendus dans les calculs
Si vous ne contrôlez pas la division par zéro ou les racines de nombres négatifs, votre programme peut produire NaN ou Infinity et « contaminer » tous les calculs suivants. N’oubliez pas de vérifier les valeurs suspectes à l’aide de Double.isNaN() et Double.isInfinite() — cela vous aidera à éviter de mauvaises surprises.
Erreur n° 3 : utiliser NaN comme « marqueur »
Certains débutants utilisent NaN comme valeur « spéciale » pour signaler, par exemple, « si non trouvé — renvoyer NaN ». Mais souvenez‑vous : NaN est piégeux, et la comparaison avec == ne fonctionnera pas ! Vérifiez uniquement via des méthodes dédiées.
Erreur n° 4 : attendre une exception lors d’une division par 0.0
Contrairement à la division entière, la division par 0.0 pour un double ne provoque pas d’erreur, mais retourne Infinity ou NaN. Cela peut mener à des bugs silencieux et difficiles à détecter — il y a un résultat, mais ce n’est pas celui que vous attendiez.
GO TO FULL VERSION