Nombres en virgules flottantes

En plus des nombres naturels (\(\mathbb{N}\)) et des nombres relatifs (\(\mathbb{Z}\)) il y a les nombres rationnels (\(\mathbb{Q}\)) et les nombres réels (\(\mathbb{R}\)). Ces deux derniers ensembles contiennent des nombres qui ne sont forcément entiers et on les représente souvent avec une virgule (ou un point décimal).

!!! note Les nombres rationnels (\(\mathbb{Q}\)) peuvent tous être représentés par une fraction de deux nombres relatifs et il existe des fonctions informatiques qui utilise cette représentation, mais nous n’aborderons pas ce point dans ce cours.

Dans l’arithmétique traditionnelle en base 10, le nombre \(123.45\) peut se lire comme:

\[ 1 \cdot 10^{2} + 2 \cdot 10^{1} + 3 \cdot 10^{0} + 4 \cdot 10^{-1} + 5 \cdot 10^{-2} \]

C’est juste une extension des nombres entiers avec des puissances de 10 négatives dès qu’on passe la virgule. Pour la représentation binaire, nous pouvons transposer cette idée en utilisant des puissances de 2 à la place des puissances de 10. Le nombre \(1010.11_{2}\) peut se lire comme :

\[ 1 \cdot 2^{3} + 0 \cdot 2^{2} + 1 \cdot 2^{1} + 0 \cdot 2^{0} + 1 \cdot 2^{-1} + 1 \cdot 2^{-2} \]

ce qui donne :

\[ 8 + 2 + \frac{1}{2} + \frac{1}{4} = 10.75_{10} \]

Comme pour les entiers, nous devons maintenant faire tenir ces nombres dans des cases mémoires des ordinateurs. Supposons que nous souhaitions faire tenir ces nombres sur 3 bits. Une première idée consiste à fixer la position de la virgule, par exemple sur le dernier bit du nombre. Il nous reste donc 2 bits pour coder le nombre et on peut représenter les nombres \(0.0, 0.5, 1.0, 1.5 \dots 3.5\) :

Cercle des nombres en virgule fixe

Si on souhaite également représenter les nombres négatifs, on peut reprendre le concept du complément à deux comme illustré sur la figure suivante.

Cercle des nombres en virgule fixe (complément à deux)

La représentation en virgule fixe est certes simple, mais elle limite beaucoup l’éventail des valeurs que nous pouvons représenter et nous ne pouvons ni représenter de très petits nombres, ni de très grands. Les mathématiciens, les scientifiques et les ingénieurs étaient aussi confrontés à cette problématique et ils ont inventé la notation scientifique. Dans cette notation, un nombre est représenté par :

\[ \pm m \cdot 10^{e} \]

Le nombre \(m\) est appelé la mantisse et sa valeur est entre \(1\) et \(10\) non compris (\(1 \leq m < 10\)). Le nombre \(e\) est appelé l’exposant.

Grâce à cette notation, nous pouvons représenter de très petits nombres
Le rayon d’un atome d’hydrogène mesure \(0.000000000053\, \mathrm{m}\) mais c’est plus simple d’écrire \(5.3 \cdot 10^{-11}\, \mathrm{m}\). Idem pour de très grands nombres : La distance entre la terre et le soleil est d’environ \(150000000000\, \mathrm{m}\), mais c’est plus simple d’écrire \(1.5 \cdot 10^{11}\, \mathrm{m}\).

Dans les ordinateurs, nous nous sommes inspiré de cette notation pour représenter les nombres en virgules flottantes (ou floating point). Nous utilisons naturellement des puissances de 2 à la place des puissances de 10, mais l’idée et la même. Par exemple, on peut représenter le nombre \(1011000000000000_2\) par \(1.011_2 \cdot 2^{15}\) (en base 10, ça fait \(45056 = 1.375 \cdot 2^{15}\)).

Tous les nombres à virgules peuvent donc être représentés par :

\[ \pm m \cdot 2^{e}\, \mathsf{avec}\, 1 \leq m < 2 \]

Nous devons connaître :

  • le signe du nombre;
  • la mantisse entre 1 et 2 non compris avec la précision souhaitée;
  • l’exposant qui peut être positif ou négatif.

Nous pouvons illustrer avec un exemple :

\(42.5 = +1.328125 \cdot 2^{5}\). Le signe est positif, la mantisse est \(1.328125\) (ou \(1.010101_2\) en base 2) et l’exposant est \(5\).

Pour certains nombres, c’est plus difficile. Par exemple \(0.1 \approx 1.600000023841858\dots 2^{-4}\). Le signe et l’exposant sont définis, mais la mantisse contient un nombre infini de chiffres après la virgule. En binaire, on note que mantisse est \(1.1\overline{0011}_2\) (avec les 4 derniers bits qui se répètent à l’infini). On voit donc que l’ordinateur ne peut pas représenter tous les nombres à virgule et il doit se contenter d’approximer la plupart des nombres.

Pour utiliser les nombres à virgules dans les ordinateurs, il nous reste à définir le format sous lequel on enregistre les nombres en mémoire. Pour cela, il existe le format standard IEEE 754. Cette norme décrit les formats suivants:

  • simple précision (32 bits)
  • double précision (64 bits)
  • quadruple précision (128 bits)

Avec le format simple précision, les 32 bits sont répartis comme suit :

  • le bit de poids fort (bit 31) représente le signe du nombre. Ce bit est à zéro pour un nombre positif et à un pour un nombre négatif.
  • les 8 bits suivants (bits 23 à 30) représentent l’exposant. L’exposant peut être positif ou négatif et on aurait pu le représenter en complément à 2, mais en l’occurrence, l’exposant est codé avec un biais (offset) de 127. Ca signifie qu’un exposant de \(0\) est code par \(127\) ou \(7\mathrm{F}_{16}\) (en binaire). La valeur \(0\) étant réservée pour le nombre \(0.0\) et \(\mathrm{FF}_{16}\) étant réservée pour les nombres spéciaux, le plus petit exposant possible est donc de \(-126\) (codé par \(127-126 = 1\)) et le plus grand exposant est \(127\) (codé par \(127 + 120 = 254 = \mathrm{FE}_{16}\)).
  • les 23 derniers bits représentent la mantisse. Comme cette dernière est comprise entre \(1\) et \(2\) non compris, il y a toujours un seul \(1\) à gauche du point décimal et ce \(1\) ne fait partie des 23 derniers bits.

IEEE 754 - simple précision

Nous pouvons illustrer l’encodage de nombres à virgules avec l’exemple de \(42.5\) vu précédemment :

  • \(42.5\) est positif, donc le bit de signe est \(0\)
  • \(42.5 = +1.328125 \cdot 2^{5}\), donc l’exposant est \(5\). Avec le biais de \(127\), cet exposant est codé par \(132\) ou \(10000100_2\).
  • la mantisse est \(1.010101_2\); on ignore le 1 à gauche de la virgule et on complète avec les \(0\) et les 23 derniers bits sont donc \(01010100000000000000000_2\).

42.5 en IEEE 754 simple précision

En hexadécimal, ça donne \(422\mathrm{A}\,0000_{16}\).

La norme IEEE 754 définit également quelques nombres spéciaux :

  • Le zéro (\(0.0\)) est codé avec tous les bits à zéro.
  • L’infini est représenté par un exposant avec tous les bits à \(1\) et la mantisse à \(0\). Le signe permet de représenter \(+\infty\) et \(-\infty\).
  • Si l’exposant a tous les bits à \(1\) mais que la mantisse est différente de zéro, alors ça représente le nombre \(NaN\) (Not a Number).
Format IEEE Nombre de bits pour la mantisse Nombre de bits pour l’exposant biais (offset)
Single precision 23 8 127
Double precision 52 11 1023
Quadruple precision 112 15 16383

Caution

Attention, en quadruple précision, contrairement aux autres formats, le \(1\) à gauche du point décimal n’est pas omis et est toujours présent dans la mantisse.