Codage de nombres à virgule

Nous savons coder les nombres entiers, positifs ou négatifs, en binaire.

Comment coder un nombre décimal ?

I. Notation "scientifique"

Pour débuter, précisons un peu de vocabulaire. Le nombre $123,45$ peut aussi s'écrire sous forme scientifique :

$$1,2345 \times 10^{+2}$$

Avec :

  • $+$ qui est le signe
  • $1,2345$ qui est la mantisse
  • $+2$ qui est l'exposant

Dans la pratique on écrit le nombre sous forme scientifique en binaire et on utilise des puissances de $2$ au lieu des puissances de $10$.

Par exemple :

$$6,75 = 4 + 2 + 0,5 + 0,25 = 1 \times 2^2 + 1 \times 2^1 + 0 \times 2^0 + 1 \times 2^{-1} + 1 \times 2^{-2}$$

On rappelle en effet que $2^{-1} = \frac{1}{2^1} = 0,5$ et $2^{-2} = \frac{1}{2^2} = 0,25$

On a donc :

$$ 6,75_{10} = 110,11_2$$

Pour obtenir l'équivalent d'une notation scientifique, on ajoute une puissance de $2$ (on doit ici décaler de deux crans): $$ + 110,11 = + 1,1011 \times 2^2$$

On a donc pour l'écriture binaire de $6,75$ :

  • un signe $+$
  • une mantisse de $1,1011$
  • un exposant $+2$

Exercice 1 :

Ecrire l'écriture scientifique en binaire des nombres suivants :

  • $3,5$ :
  • $12,25$ :
  • $5,125$ :
  • $17,625$ :

Exercice 2 :

Ecrire l'écriture scientifique en binaire des nombres suivants :

  • $0,25$ :
  • $0,0625$ :

Exercice 3 :

Ecrire l'écriture scientifique en binaire de $0,1$. Que remarque-t-on ?

Ce dernier exemple, montre l'un des problèmes de la notation binaire : certains nombres nécessitent pour être écrits, une infinité de coefficients. C'est le cas de $0,1$.

On peut s'en rendre compte avec python :

In [3]:
0.1 + 0.2
Out[3]:
0.30000000000000004

II. Nombre en virgule flottante

Une fois obtenue cette "notation scientifique" on doit stocker le nombre.

Le nombre de bits réservé dépend du degré de précision du langage utilisé. On parle de simple précision quand le nombre est codé sur $32$ bits, de double précision quand il l'est sur $64$ bits.

Dans le cas d'une simple précision, les bits on la signification suivante :

simplePrecision.PNG

  • Le signe est codé sur $1$ bit ($0$ pour les positifs, $1$ pour les négatifs),
  • L’exposant est codé sur $8$ bits,
  • la mantisse est codée sur $23$ bits

Une astuce : en binaire, le premier chiffre de la mantisse est toujours un $1$ (pourquoi ?). Inutile de le coder : on a donc une précision de 24 bits pour la mantisse.

Pour l'exposant, on évite de manipuler des puissances négatives : toutes les puissances sont décalée de $127$ (car on est sur $8$ bits) afin d'être encodée de façon positive.

Dans le cas de $6,75$, on avait vu que l'exposant valait $2$. On va coder une valeur de $2 + 127 = 129$ soit $10000001$ en binaire.

L'écriture de $6,75$ sera donc :

Signe Exposant Mantisse
$0$ $10000001$ $10110000000000000000000$

Exercice 4 :

On sait que $3,5_{10}$ s'écrit $11,1 = 1,11 \times 2^1$ en binaire. Ecrire son encodage en nombre flottant à simple précision.

Exercice 5 :

Ecrire l'encodage en nombre flottant à simple précision de $8,375$.

On peut retrouver le nombre de départ à partir de son écriture en "flottant". On pose $\pm$ pour le signe, $e$ pour l'exposant codé par l'ordinateur et $M$ comme mantisse.

  • Si l'exposant est non nul et différent de $255$ on parle de nombre normalisé. Le nombre vaut : $$\pm \: 1,M \times \: 2^{e-127}$$
  • Si l'exposant vaut $255$:
    • Si la mantisse est nulle, on code ainsi $\pm \infty$ selon le signe
    • Si la mantisse est non nulle, on code une erreur : Not a Number ou NaN. Cela permet par exemple de gérer la division par $0$...
  • Si l'exposant est nul :
    • Si la mantisse est nulle, on code $0$
    • Si la mantisse est non nulle, on parle de nombre dénormalisé. Ce sont les nombres très proches de $0$
      • on prend comme exposant $-126$
      • le bit implicite est laissé à $0$
      • On a donc : $$\pm \: 0,M \times \: 2^{-126}$$

Ainsi le plus petit nombre manipulable dans le cas d'une simple précision est :

Signe Exposant Mantisse
$0$ $00000000$ $00000000000000000000001$

Il vaut : $$ 0,1 \times 2^{-126} \approx 1.1754943508222876 \times 10^{-39}$$

Remarque : Python fournit les informations suivantes sur sa gestion des "flottants" :

In [9]:
import sys
sys.float_info
Out[9]:
sys.float_info(max=1.7976931348623157e+308, max_exp=1024, max_10_exp=308, min=2.2250738585072014e-308, min_exp=-1021, min_10_exp=-307, dig=15, mant_dig=53, epsilon=2.220446049250313e-16, radix=2, rounds=1)

Voir https://docs.python.org/3/library/sys.html#sys.float_info pour l'interprétation. On peut toutefois repérer :

  • max=1.7976931348623157e+308 : le plus grand nombre flottant "codable"
  • max_exp=1024 : le plus grand exposant, exprimé en décimal. Comme $1024 = 2^{10}$ on en déduit que l'exposant est codé sur $10$ bits
  • min=2.2250738585072014e-308 : le plus petit nombre flottant "codable"
  • mant_dig=53 : le nombre de bits de la mantisse ($53$ ici)

Comme le signe est sur $1$ bit, que l'exposant est sur $10$ et la mantisse sur $53$, on a un total de $64$ bits utilisés : python code les flottants en double précision.