Comparaison de doubles

Les ordinateurs ne font pas d’erreurs. Pourtant, pour un ordinateur, 0,2 peut être différent de 1−0.8 !

Qu’est-ce qui cloche avec ce code ? À votre avis, quel sera le résultat de ce traitement hyper simple ?

  double dix = 1.0;
  double huit = 0.8;
  double deux = 0.2;

  if (dix - huit == deux)
  {
    Console.WriteLine("Ok");
  }
  else
  {
    Console.WriteLine("{0} != {1}", dix - huit, deux);
  }

Quand on débuggue un code qui de ce genre, il peut être assez difficile de comprendre ce qui se passe ! Car le code précédent affichera un message d’erreur et non «OK». La faute au codage des nombres à virgule flottante.

Explication

Il faut se rappeler de ses premiers cours d’informatique, où on parlait de mantisse et d’exposant, de codage des nombres à virgule flottante. Souvenirs souvenirs.

C’est vraiment pas de bol, car 0.2 en décimal ne peut pas être représenté de manière exacte en utilisant un codage en virgule flottante. Voyez vous même, ça ressemble pas mal au 0,33333333333 du monde décimal non ? Il se trouve qu’on n’est alors pas à l’abri d’une erreur d’arrondi.

La solution

Tout d’abord, merci à Gendarme, un logiciel libre permettant de détecter des problèmes dans un code source Mono ou .Net. Ce logiciel est un peu l’équivalent libre de FxCop avec un jeu de règles différent.
Gendarme dispose donc d’une règle permet de trouver automatiquement ce genre de cas problématiques et sa documentation propose une solution.

  // declaration, avec une valeur dépendant du calcul effectué et des données
  private static readonly double epsilon = 1e-6;
  
  // dans le code
  if (Math.Abs ((dix - huit) - deux) > epsilon) {
    return false;
  }

C’est lourd, c’est moche, et la précision demandée dépend des données comparées. Mais ça a le mérite de marcher et de prendre en compte les erreurs d’arrondi. Pour les éviter, il existe le type Decimal en .Net et BigDecimal en Java, mais leur utilisation est pénalisante en temps de calcul (les processeurs sont optimisés pour réaliser des calculs en virgule flottante).

Commentaires

2 Comments sur « Comparaison de doubles »

  1. LTH dit :

    normal y’a un bug ds ton code essaye avec ça…

    double dix = 10.0;
    double huit = 8.0;
    double deux = 2.0;

    désolé, c’était juste pour le fun, évidement tu as raison, on ne le répètera jamais assez, pas de test d’égalité sur des flottants

  2. Ghusse dit :

    C’est juste qu’écrire zeroVirguleDeux, je trouvais ça un peu long et moins parlant.

Laisser un commentaire