Représentation des décimaux : les flottants
Le Programme
Contexte
Nous avons appris à encoder des nombres entiers naturels, et relatifs, et nous avons vu que les limites physiques des machines imposaient des limites sur l'étendue des valeurs. Par exemple, sur un octet on dispose de 2^8 = 256 valeurs distinctes qui permettent d'encoder:
- des nombres entiers naturels entre 0 et 255.
- des nombres entiers relatifs entre -128 et 127.
Maintenant que nous allons tenter de coder les réels, les limites de notre machine vont encore entraîner des limites sur l'étendue des valeurs, mais également sur la précision des valeurs.
À Faire
Effectuez le calcul 0.2 + 0.1 en Python :
>>> 0.2 + 0.1
???
Testez l'égalité suivante :
>>> 0.2 + 0.1 == 0.3
???
Il n'est pas possible de coder un nombre décimal en valeur exacte en base 2.
On obtient une approximation du nombre décimal, et non le nombre en lui même.
Cette approximation est appelée nombre en virgule flottante et correspond au type float en Python.
Ainsi, un calcul avec des nombres à virgule ne peut-être qu'approximatif. Cependant plus on augmente la taille du registre du processeur et plus nous pourrons représenter de valeurs, et plus nos calculs gagneront en précision.
Codage de la virgule
Il existe deux façons de coder les nombres réels, en virgule fixe ou virgule flottante.
Virgule fixe
Le codage en virgule fixe consiste à garder un nombre fixe de chiffes après la virgule.
Pour une représentation sur n bits, on fixe e bits pour la partie entière et v bits pour la partie décimale où e + v = n.
Exemple : Sur un octet, on peut utiliser 4 bits pour la partie entière et 4 bits pour la partie décimale.
Ainsi, 0101 1011 a pour valeur : 2^2 + 2^0 + 2^{-1} + 2^{-3} + 2^{-4} = 4 + 1 + 0.5 + 0.125 + 0.0625 = 5.6875
C’est extrêmement simple. Cette manière de faire s’appelle virgule fixe, car la position de la virgule est connue d’avance.
L’inconvénient de cette méthode est que, pour un nombre avec peu de chiffres après la virgule, on perd un espace de stockage significatif. Si le nombre en question est 0110 1000, on perd trois bits “inutilement”.
À Faire
Représenter les valeurs suivants sur 8 bits, en virgule fixe :
- 7,75
- 0,1
Virgule flottante
Notation scientifique
Cette écriture se base sur la notation scientifique des nombres : \pm a \times 10^n où 1 \le a \lt 10 et n \in \mathbb{Z}^*
Par exemple :
- 12 s'écrit :
1,2 \times 10^1 - -85 s'écrit :
-8,5 \times 10^1 - 0,0123 s'écrit :
1,23 \times 10^{-2}
Le terme exposant correspond à la puissance de 10, et le terme mantisse correspond à la partie décimale. Ainsi, dans « 1,23 × 102 » :
- la mantisse (ou significande) est « 1,23 » ;
- l'exposant est « -2 ».
À Faire
Exprimez les nombres suivants en notation scientifique :
- La distance
dentre la Lune et la Terre est de 384 400km - Le poids
pdu moustique Tigre est d'environ 0, 000 001 07kg.
Notation scientifique binaire
Un nombre binaire à virgule de quatre chiffres n_1n_0,n_{-1}n_{-2} correspond au nombre décimal n_1 \times 2^1 + n_0 \times 2^0 + n_{-1} \times 2^{-1} + n_{-2} \times 2^{-2}.
On peut ainsi avoir une notation scientifique binaire : n_1n_0,n_{-1}n_{-2} en binaire peut se noter n_1,n_0n_{-1}n_{-2} \times 2^1.
Par exemple :
11_2 = 1,1_2 \times 2^10,11_2 = 1,1_2 \times 2^{-1}
N.B : Dans le cas de la notation scientifique binaire, le nombre avant la virgule doit être compris entre 1_2 inclus et 10_2 exclus (c'est-à-dire 2 exclus), c'est-à-dire que sa partie entière est nécessairement 1.
Passage de l'écriture décimale à l'écriture binaire pour un nombre flottant
Si vous avez bien suivi, vous savez qu'un nombre est composé de deux parties :
- partie entière, à gauche de la virgule
- partie décimale (ou fractionnaire), à droite de la virgule
On traite ces deux parties indépendamment l'une de l'autre :
-
La partie entière va s'écrire comme nous l'avons appris précédemment.
-
Pour la partie décimale, on va procéder par multiplications par 2 successivement.
- Après chaque multiplication, le résultat est reporté sans sa partie entière
- On poursuit les multiplications jusqu'à obtenir 1 comme résultat.
- On prend la partie entière de chaque résultat, de haut en bas, pour obtenir l'écriture binaire du nombre.
Exemple :
0,6875 \times 2 = 1,3750,375 \times 2 = 0,750,75 \times 2 = 1,50,5 \times 2 = 1
0,6875 s'écrit donc 0,10112
Principe du codage en virgule flottante
Un nombre flottant est formé de trois éléments : la mantisse, l'exposant et le signe.
\begin{aligned}
s.m \times 2^e
\end{aligned}
- Le bit de poids fort
sest le bit de signe : si ce bit est à 1, le nombre est négatif, et s’il est à 0, le nombre est positif. - Les
ebits suivants représentent l'exposant biaisé (sauf valeur spéciale), - Les
mbits suivants (mbits de poids faible) représentent la mantisse.
Exemple :
Supposons un nombre flottant codé sur un octet utilisant 1 bit de signe, 3 bits pour l'exposant et 4 bits pour la mantisse: 1 101 1011
sest le signe représenté par le bit de poids fort. Notre codage représente donc un nombre négatif.eest l'exposant biaisé, représenté par un entier relatif décalé et non en complément à 2.- Ce décalage est de
2^{|e|-1} - 1(|e|représente le nombre de bits utilisé pour coder l'exposant). - L'exposant a pour valeur
101codé sur 3 bits. Il doit être décalé de2^2 - 1 = 3. - Ainsi, puisque
101_2 = 5_{10}, l'exposant101correspond à un exposant de5 - 3 = 2.
- Ce décalage est de
mest la mantisse, elle représente en binaire uniquement les chiffres après la virgule.- Dans notre exemple, la mantisse est
1011, - Soit
m = 1 + 1 \times 2^{-1} + 1 \times 2^{-3} + 1 \times 2^{-4} = 1 + \frac{1}{2} + \frac{1}{8} + \frac{1}{16} = 1,6875
- Dans notre exemple, la mantisse est
Le code 1 101 1011 sur un octet utilisant 1 bit de signe, 3 bits pour l'exposant et 4 bits pour la mantisse représente donc: -1,6875 \times 2^2 = -6,75
La norme IEEE 754
L'IEEE 754 est une norme pour la représentation des nombres à virgule flottante en binaire. Elle est la norme la plus employée actuellement pour le calcul des nombres à virgule flottante dans le domaine informatique.
Cette norme définit notamment 2 formats pour représenter des nombres à virgule flottante:
- simple précision (32 bits : 1 bit de signe, 8 bits d'exposant (-126 à 127), 23 bits de mantisse),
- double précision (64 bits : 1 bit de signe, 11 bits d'exposant (-1022 à 1023), 52 bits de mantisse)
Valeurs remarquables
Chaque norme défini aussi des valeurs spéciales, par exemple en double précision:
- le zéro positif: +0 =
0 00000000000 0000000000000000000000000000000000000000000000000000, - le zéro négatif: -0 =
1 00000000000 0000000000000000000000000000000000000000000000000000, - l'infini positif: +∞ =
0 11111111111 0000000000000000000000000000000000000000000000000000, - l'infini négatif: +∞ =
1 11111111111 0000000000000000000000000000000000000000000000000000
Impossibilité de coder tous les nombres réels
Voici l’écriture binaire en format double précision de deux flottants.
| Nombre flottant | Représentation format simple précision (mantisse sur 23 bits) |
|---|---|
2.0^{(1)} |
0 10000000 00000000000000000000000 |
2.000000238418579^{(2)} |
0 10000000 00000000000000000000001 |
Calcul de (1) :
\begin{aligned}
n & = s.m \times 2^e \\
e & = e_{biaisé} - décalage \\
e_{biaisé} & = 10000000_2 = 2^7 \\
décalage & = 2^{8 - 1} - 1 \\
e & = 2^7 - (2^7 - 1) \\
m & = 1 + 0 \\
n & = 1 \times 2^1 = 2.0
\end{aligned}
Calcul de (2) :
\begin{aligned}
n & = s.m \times 2^e \\
e & = e_{biaisé} - décalage \\
e_{biaisé} & = 10000000_2 = 2^7 \\
décalage & = 2^{8 - 1} - 1 \\
e & = 2^7 - (2^7 - 1) \\
m & = 1 + 2^{-23} \\
n & = (1 + 2^{-23})\times 2^1 = 2 + 2^{-22} = 2.000000238418579
\end{aligned}
On comprend aisément qu’il n’y a pas de flottant entre 2.0 et 2.000000238418579.
Le flottant 2.000000238418579 est donc représenté comme le suivant de 2.0.
La précision possible avec une mantisse sur 23 bits se situe au niveau dernier bit qui vaut
2^{-23} . On ne peut pas trouver un flottant compris entre les deux.
Conclusion
Les nombres flottants sont une représentation approximative des nombres réels dans un ordinateur. En particulier, il n’est pas possible de représenter de manière exacte en machine tous les nombres réels. La manipulation de nombres réels par un langage informatique est donc à prendre avec précaution car elle peut engendrer des résultats surprenants, en particulier il ne faut jamais tester l’égalité entre deux flottants.
Exercices
Exercice
On considère des nombres flottants encodés sur un octet avec dans l'ordre:
- 1 bit de signe,
- 3 bits d'exposant,
- 4 bits de mantisse.
- Trouver les nombres à virgule représentés par les mots binaires suivant:
0111 10001001 0001
- Donner les représentations binaires des nombres flottants suivants:
- 2,5 (qui est égal à
1,25 \times 2). - -1,125.
- 2,5 (qui est égal à
- Avec cet encodage à 8 bits:
- Quel est le plus grand nombre à virgules que l'on peut représenter ?
- Quel est le plus petit nombre à virgule, donc négatif ?
- Quel est le plus petit nombre à virgule strictement positif que l'on peut représenter ?
Exercice
- Quelles sont les valeurs attendues des instructions suivantes ?
>>> # Cas 1
>>> from math import sqrt
>>> sqrt(2.0)**2 == 2.00
???
>>> # Cas 2
>>> 9007199254740992.0 + 1
???
>>> # Cas 3
>>> 1.2 - 1.0
???
>>> # Cas 4
>>> 0.5 - 0.2 - 0.2 - 0.1
???
>>> # Cas 5
>>> 9007199254740992.0 + 1.0 + 1.0
???
>>> # Cas 6
>>> 1.0 + 1.0 + 9007199254740992.0
???
- Copier et exécuter les instructions précédentes et comparer avec les résultats attendus.
Exercice
On considère le programme suivant:
a = 0.0
for loop in range(0,10):
a = a + 0.1
print(a)
- Si l'on calculait sur des nombres rationnels exacts, que se passerait-il lors de l'exécution de ce programme ?
- Écrire ce programme et l'exécuter. Que constate-t-on ?
- Vérifier avec le convertisseur en ligne que la représentation binaire de 0,1 est
0 01111111011 1001100110011001100110011001100110011001100110011010. - Quel nombre décimal cette représentation désigne-t-elle en réalité Quel nombre décimal cette représentation désigne-t-elle en réalité ? Expliquer le résultat obtenu.
