Plan du site  
pixel
pixel

Articles - Étudiants SUPINFO

Les problèmes que posent les nombres décimaux en programmation

Par Théodore CHOLIN Publié le 31/10/2015 à 19:48:08 Noter cet article:
(0 votes)
Avis favorable du comité de lecture

Une expérience étonnante

Pour comprendre d'emblée quels genre de problèmes peuvent poser les nombres décimaux, il est possible de faire une expérience très simple :

  • Dans votre navigateur web favori, ouvrez la console JavaScript (f12 en général, puis cherchez l'onglet console).

  • Tapez le calcul suivant : 0.1+0.2 et faites entrée.

  • Observez avec effarement le résultat: 0.30000000000000004

Incroyable non ? Et cela se produit avec plein de calculs avec nombres décimaux. Il se trouve que tous les langages de programmation qui permettent de faire des calculs avec des nombres décimaux en entrée ont ce problème, pas seulement le JavaScript. Cependant, certains langages ont mis au point des solutions quand d'autres ne proposent rien nativement. Nous allons voir dans cet article la raison de ces résultats, et quelles sont les solutions à notre disposition si nous voulons des valeurs décimales exactes.

Qu'est-ce que la "virgule flottante" ?

La raison fondamentale de ces imprécisions vient de la façon dont les nombres sont stockés. En JavaScript, les nombres sont toujours de types "virgule flottante binaire 64 bits".

La virgule flottante consiste à représenter un nombre par un signe s (égal à 0 ou 1 dans notre cas), une mantisse m et un exposant e. Un tel triplet représente un nombre réel s.m.b^e où b est la base de représentation. Dans notre cas, la base est 2 puisque les nombres sont stockés au format binaire.

Par exemple, au format 32 bits à virgule flottante, le nombre -248.75 sera stocké de la façon suivante :

Mais pour nous concentrer sur le problème qui nous intéresse, nous allons représenter les nombres sous un format plus intuitif. Le nombre -248.75 peut s'écrire :

-11111000.11

Il se trouve que ce nombre possède une représentation exacte à la fois en base 2 et en base 10. Mais c'est très loin d'être le cas de tous les nombres décimaux non entiers.

Explication du phénomène observé

Prenons le nombre 0.1(base 10). Il est égal à "un dixième de 1". Mais comment représenter exactement un dixième en binaire ? Eh bien c'est tout bonnement impossible. On ne peut en avoir que des représentations approchées.

Avec un nombre binaire de type double précision (64 bits), 0.1 donne :

0.0001100110011001100110011001100110011001100110011001101

On voit ici que le même schéma se reproduit à l'infini après la virgule. On voit donc que le nombre décimal 0.1 ne possède pas de représentation binaire exacte avec un nombre fini de chiffres après la virgule. Le nombre que l'on obtient est donc forcément tronqué, et donc inexact.

Pour 0.2, cela donne en binaire double précision :

0.001100110011001100110011001100110011001100110011001101

Si on ajoute ces deux nombres (qui sont donc des représentations binaire approchées de 0.1 et 0.2) et que l'on fait le calcul nous même, on trouve :

0.0001100110011001100110011001100110011001100110011001101 
+ 
0.001100110011001100110011001100110011001100110011001101 
= 
0.0100110011001100110011001100110011001100110011001100111

Ce qui donne en décimal :

0.3000000000000000166533453693773481063544750213623046875

En réalité JavaScript fait quelque-chose d'un petit peu différent au moment de l'addition puisque rappellez vous, il fonctionne avec une mantisse et un exposant (plus précisément il utilise la norme IEEE 754 ). Nous n'allons pas entrer dans les détails mais il se trouve que JavaScript trouve en fait :

0.0100110011001100110011001100110011001100110011001101

Ce qui donne en décimal :

0.3000000000000000444089209850062616169452667236328125

Or, JavaScript affiche 17 décimales maximum. Ceci explique donc pourquoi dans la console nous pouvons voir le nombre arrondi :

0.30000000000000004

Un problème global

Il est bien important de comprendre que ce problème n'est pas spécifique du binaire. La base 10, par exemple, possède exactement le même problème.

Pour comprendre pourquoi intéressons-nous à la base 3. Le nombre 0.1 en base 3 est égal à "un tiers" de 1. Or, 1/3 en base 10 s'écrit : 0.333333333... avec une infinité de 3. On voit donc bien qu'en base 10 également, nous ne pouvons pas représenter de façon exacte certains nombres non entiers qui par ailleurs s'écrivent de manière exacte dans d'autres bases.

Solutions

Avant de parler de solutions il faut identifier le besoin. Dans beaucoup de cas, il n'est pas nécessaire d'avoir une précision exacte quand on utilise des nombres dans un programme. Mais dans certains cas, par exemple quand on a besoin de faire des calculs financiers, il nous faut impérativement pouvoir manipuler des valeurs décimales exactes.

Si l'on a un tel besoin les solutions varient selon les langages.

Solutions natives

Certains langages proposent des types de variables spécialement prévus pour représenter de façon exacte les nombres décimaux. Par exemple en C# il existe le type Decimal, qui utilise un systeme décimal à virgule flottante. En contrepartie les opérations faites avec ce type sont beaucoup plus lentes qu'avec les types Float ou Double.

Solutions non natives

En JavaScript ou tout autre langage qui ne propose pas de solution native, il est forcément nécessaire de bricoler un peu :

  • Multiplier les nombres de façon à ce qu'ils deviennent entiers, faire le calcul, puis diviser le résultat. Par exemple pour 0.1 + 0.2 : var x = (0.1 * 10 + 0.2 * 10) / 10; // x sera 0.3

  • Si l'on considère que l'on parle d'argent : faire les calculs sur les centimes à part puis les ré-injecter à la fin.

  • Utiliser des bibliothèques externes. Par exemple en JavaScript : http://jsfromhell.com/classes/bignumber

A propos de SUPINFO | Contacts & adresses | Enseigner à SUPINFO | Presse | Conditions d'utilisation & Copyright | Respect de la vie privée | Investir
Logo de la société Cisco, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société IBM, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Sun-Oracle, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Apple, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Sybase, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Novell, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Intel, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Accenture, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société SAP, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Prometric, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Toeic, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo du IT Academy Program par Microsoft, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management

SUPINFO International University
Ecole d'Informatique - IT School
École Supérieure d'Informatique de Paris, leader en France
La Grande Ecole de l'informatique, du numérique et du management
Fondée en 1965, reconnue par l'État. Titre Bac+5 certifié au niveau I.
SUPINFO International University is globally operated by EDUCINVEST Belgium - Avenue Louise, 534 - 1050 Brussels