informatique commune
Chapitre 4
Représentation des nombres
Dans ce chapitre, nous allons nous intéresser à la façon dont un nombre (entier ou réel) peut être représenté à
l’intérieur d’un ordinateur. Nous savons déjà 1 que la mémoire des ordinateurs est découpée en blocs de 8 bits
(un octet) et qu’un processeur 64 bits va travailler avec des paquets de 8 octets (contre 4 pour les processeurs
32 bits). Nous disposons de 64 bits pour représenter un nombre ; il est donc naturel de faire intervenir la
décomposition de ce dernier en base 2 pour le représenter en mémoire.
1. Représentation dans une base
Nous sommes habitués depuis l’enfance à utiliser l’écriture en base 10 des entiers : par exemple, 2985 représente
le nombre 2 × 103 + 9 × 102 + 8 × 10 + 5 × 100 . Mais plus généralement, pour tout entier b > 2 on peut définir
la représentation en base b d’un entier en convenant que l’écriture (ap ap−1 · · · a0 )b représente le nombre :
ap × bp + ap−1 × bp−1 + · · · + a1 × b + a0 .
Pour s’assurer de l’unicité de l’écriture d’un entier dans une base donnée, il est nécessaire en outre d’imposer :
∀k ∈ ~0, p, ak ∈ ~0, p − 1 et ap , 0. Ainsi, en base 3 par exemple, seuls les chiffres 0, 1 et 2 seront utilisés, et le
nombre (210122)3 représente l’entier 2 × 35 + 34 + 32 + 2 × 3 + 2, c’est à dire 584 2 .
Écriture en base 2
Dans le cas particulier de la base 2, seuls les chiffres 0 et 1 sont donc utilisés, ce qui rend les opérations usuelles
particulièrement simples à réaliser. En effet, les algorithmes de calcul appris à l’école primaire en base 10
(addition, soustraction, multiplication, division) se généralisent en base 2.
1 0 1 0
× 1 0 1
1 0 1 1 0 1 1 1 0 1 0
+ 1 0 1 0 0 1 1 0 1 0 · ·
1 0 0 0 0 1 0 0 1 1 0 0 1 0
Figure 1 – Un exemple d’addition et de multiplication en base 2
Exercice 1 Réaliser les opérations suivantes en base 2, sans passer par la base 10, à l’aide des algorithmes
appris à l’école primaire :
(101010)2 + (11000)2 (110101)2 − (11001)2 (11101)2 × (1011)2 (1100101)2 ÷ (1011)2
Changement de base p
X
Pour convertir le nombre x = (ap ap−1 · · · a1 a0 )b en base 10, il suffit d’appliquer la formule x = ak bk . Ceci peut
être aisément effectué à l’aide du script python suivant : k=0
def base10(x, b=2):
s = 0
k = len(x) − 1
for a in x:
s += int(a) * b ** k
k −= 1
return s
1. voir le chapitre 1.
2. on conviendra que par défaut les nombres sont écrits en base 10 ; ainsi on écrira 584 en lieu et place de (584)10 .
Jean-Pierre Becirspahic
4.2 informatique commune
Attention, pour définir un nombre dans une base non décimale on ne peut utiliser le type int car tout nombre
entré au clavier est implicitement supposé écrit en base 10. C’est la raison pour laquelle, dans cette fonction, les
nombres que l’on souhaite convertir sont représentés par une chaîne de caractères, et la fonction int permet de
les convertir en leur équivalent numérique. Cette fonction ne permet donc pas d’utiliser des bases supérieures à
10 puisque seuls les caractères de '0' à '9' peuvent être convertis en leur équivalent numérique.
Par exemple :
In [1]: base10('210122', b=3)
Out[1]: 584
In [2]: base10('1011011')
Out[2]: 91
On peut observer que la réponse numérique obtenue, indépendamment de sa représentation machine dont on parlera
plus tard, est implicitement représentée en base 10 dans la console. Pour des raisons évidentes, l’interaction
numérique entre la machine et l’utilisateur se fait en base 10.
Schéma de Horner
Il est possible d’effectuer ce calcul sans calcul de puissance en appliquant la méthode de Horner. Cette dernière
consiste à itérer la suite finie (u0 , u1 , . . . , up ) définie par :
u0 = (ap )b , u1 = (ap ap−1 )b , ··· uk = (ap ap−1 · · · ap−k )b , ··· up = (ap ap−1 · · · a0 )b = x.
Les termes de cette suite sont liés par la récurrence uk = buk−1 + ap−k , ce qui conduit à la définition alternative
(et préférable) suivante :
def base10(x, b=2):
u = 0
for a in x:
u = b * u + int(a)
return u
Conversion réciproque
À l’inverse, pour convertir un entier écrit en base 10 en une base quelconque, il faut observer que si x =
(ap ap−1 · · · a1 a0 )b alors le quotient de la division euclidienne de x par b est égal à q = (ap ap−1 · · · a1 )b et le reste à
r = a0 puisque x = bq + r avec 0 6 r 6 b − 1. Ceci conduit à la fonction suivante :
def baseb(x, b=2):
s = ''
y = x
while y > 0:
s = str(y % b) + s
y //= b
return s
Par exemple :
In [3]: baseb(584, b=3)
Out[3]: '210122'
In [4]: baseb(91)
Out[4]: '1011011'
Le cas particulier de la base 16
Il a été dit plus haut que l’interaction homme/machine se fait implicitement en base 10. Il y a néanmoins deux
exceptions. Il est possible d’introduire directement au clavier un nombre écrit en base 2 ; il suffit de le faire
précéder des caractères 0b :
In [5]: 0b1011011
Out[5]: 91
Représentation des nombres 4.3
La seconde exception est la base 16, en faisant précéder le nombre des caractères 0x :
In [6]: 0xa27c
Out[6]: 41596
En effet, 10 × 163 + 2 × 162 + 7 × 16 + 12 = 41596.
La raison du rôle particulier que joue la base 16 en informatique provient du fait que l’écriture binaire d’un
nombre présente l’inconvénient d’être très longue à écrire, ce qui a incité les informaticiens à écrire ces nombres
dans une base plus élevée. Le choix de la base 10 pourrait paraître naturel, mais malheureusement convertir un
nombre de la base 10 à la base 2 ou inversement n’est pas chose facile. En revanche, nous allons voir que passer
de la base 2 à la base 16 est très simple à réaliser.
Pour écrire un nombre en base 16, nous avons besoin d’un caractère pour chacun des entiers de 0 à 15 ; on
complète donc les chiffres de 0 à 9 par les lettres a, b, c, d, e et f. Ainsi, (a)16 = 10, (b)16 = 11, (c)16 = 12,
(d)16 = 13, (e)16 = 14, (f)16 = 15.
Sachant que 24 = 16, tout nombre écrit en base 2 à l’aide de 4 chiffres s’écrit en base 16 à l’aide d’un seul chiffre :
(0000)2 = (0)16 (0100)2 = (4)16 (1000)2 = (8)16 (1100)2 = (c)16
(0001)2 = (1)16 (0101)2 = (5)16 (1001)2 = (9)16 (1101)2 = (d)16
(0010)2 = (2)16 (0110)2 = (6)16 (1010)2 = (a)16 (1110)2 = (e)16
(0011)2 = (3)16 (0111)2 = (7)16 (1011)2 = (b)16 (1111)2 = (f)16
Aussi, pour convertir un nombre quelconque de la base 2 à la base 16, il suffit de regrouper les chiffres qui
le composent par paquet de 4 et convertir chacun de ces paquets en un chiffre en base 16. Par exemple,
(1011 0110 1110 1001)2 = (b6e9)16 .
L’écriture hexadécimale (c’est-à-dire en base 16) est fréquemment utilisée en informatique car un octet (qui
rappelons le vaut 8 bits) sera toujours représenté par deux caractères hexadécimaux. Par exemple, une couleur
d’une page web est définie par trois octets représentant ses composantes RVB 3 . Ainsi, un navigateur web va
interpréter le code couleur (ffa500)16 comme du orange, (00ff00)16 comme du vert, ou encore (ee82ee)16
comme du violet. Potentiellement, 2563 = 16 777 216 couleurs différentes sont accessibles.
Au vu de l’importance de la base 2 et de la base 16 en informatique, il existe deux fonctions qui réalisent la
conversion vers la base 2 et vers la base 16 : les fonctions bin et hex. Ces fonctions prennent en argument un
objet de type int et retournent une chaîne de caractères (précédée des caractères 0b ou 0x).
In [7]: bin(41397)
Out[7]: '0b1010000110110101'
In [8]: hex(41397)
Out[8]: '0xa1b5'
In [9]: 0b1010000110110101 + 0xa1b5
Out[9]: 82794
2. Codification des nombres entiers
Pour pouvoir être stocké et manipulé par un ordinateur, un nombre doit être représenté par une succession de
bits. Le principal problème est la limitation de la taille du codage : un nombre mathématique peut prendre des
valeurs arbitrairement grandes, tandis que le codage dans l’ordinateur doit s’effectuer sur un nombre de bits
fixé.
2.1 Les entiers naturels
Les entiers naturels sont essentiellement utilisés pour représenter les adresses en mémoire. Un codage sur n
bits permet de représenter tous les nombres naturels compris entre 0 et 2n − 1. Ainsi, un octet permet de coder
les entiers allant de 0 = (00)16 = (0000 0000)2 à 255 = (ff)16 = (1111 1111)2 , et 64 bits (soit 8 octets) tous les
nombres allant de 0 = (0000 0000 0000 0000)16 à 264 − 1 = (ffff ffff ffff ffff)16 .
3. les composantes Rouge Vert Bleu en synthèse additive.
Jean-Pierre Becirspahic
4.4 informatique commune
Deux valeurs particulières demandent à être bien connues, la représentation des entiers de la forme 2p et ceux
de la forme 2p − 1 :
– l’entier naturel 2p est représenté par 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0
p bits
– l’entier naturel 2p − 1 est représenté par 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1
p bits
2.2 Les entiers relatifs
Pour représenter un entier relatif il est nécessaire de coder le signe de ce dernier sur un bit (0 pour les nombres
positifs et 1 pour les nombres négatifs) ; ainsi le processeur va réserver le premier bit pour le signe, et les n − 1
autres bits pour l’entier à proprement parler. Le plus grand entier relatif positif représentable sur n bits est
donc égal à 2n−1 − 1. En base 2, il s’écrit : (0 111 1111 · · · 1111)2 .
| {z }
n − 1 chiffres 1
représentation binaire de x
Figure 2 – Représentation machine d’un entier relatif positif.
Sur une architecture 64 bits le plus grand entier relatif est donc égal à 263 − 1, qu’on peut écrire en base 16 sous
la forme (7 fff · · · ffff)16 . En base 10, il vaut :
| {z }
15 chiffres f
In [1]: 0x7fffffffffffffff
Out[1]: 9223372036854775807
Le codage des nombres négatifs
On pourrait s’attendre à ce que la représentation d’un entier relatif négatif n soit un 1 (le bit de signe) suivi de
la représentation binaire de |n|. Il n’en est rien, car cette méthode possède plusieurs inconvénients :
– Le nombre 0 posséderait deux représentations (et il est toujours préférable d’avoir unicité de la représen-
tation d’un objet) ;
– L’algorithme d’addition ne s’applique qu’à des entiers de même signe et cette représentation le rendrait
inapplicable pour additionner des entiers relatifs.
C’est pourquoi on utilise le codage particulier pour représenter les entiers négatifs, dit en complément à deux :
le nombre négatif x est représenté en mémoire par la représentation binaire de l’entier (positif) 2n + x.
représentation binaire de 2n + x
Figure 3 – Représentation machine d’un entier relatif négatif.
Pour que cette représentation sur n bits commence par un 1, il est donc nécessaire que 2n + x soit compris entre
2n−1 = (1 000 0000 · · · 0000)2 et 2n − 1 = (1 111 1111 · · · 1111)2 soit :
| {z } | {z }
n − 1 chiffres n − 1 chiffres
2n−1 6 2n + x 6 2n − 1 ⇐⇒ −2n−1 6 x 6 −1
Le plus petit entier négatif représentable sur une architecture à n bits est donc égal à −2n−1 et est représenté
par : (1 000 · · · 0000)2 .
| {z }
n − 1 bits
Ainsi, les entiers relatifs représentables sur une architecture à n bits sont compris entre −2n−1 et 2n−1 − 1.
Représentation des nombres 4.5
Attention donc à bien distinguer un nombre de sa représentation, qui ne coïncide pas avec sa représentation
binaire dans le cas des nombres négatifs.
Exemple. Pour simplifier les calculs, considérons une représentation sur 8 bits (n = 8).
L’entier relatif 105 est représenté par l’octet 01101001, ce qui correspond à la représentation en base 2 de
l’entier 105 = 26 + 25 + 23 + 20 .
L’entier relatif −105 est représenté par l’octet 10010111, ce qui correspond à la représentation en base 2 de
l’entier 256 − 105 = 151 = 27 + 24 + 22 + 21 + 20 .
Exercice 2 Dans une représentation en complément à deux sur 8 bits, quels sont les entiers relatifs représen-
tés par 01101101 et par 10010010 ?
nombre représentation
0 0000......0000
1 0000......0001
··· ..............
2n−1 − 1 0111......1111
−2n−1 1000......0000
··· ..............
−1 1111......1111
Figure 4 – Les entiers relatifs codés sur n bits, classés par représentation croissante.
Addition de deux entiers relatifs
La première conséquence de la représentation par complément à deux est que tout entier de l’intervalle
~−2n−1 , 2n−1 − 1 possède une unique représentation. Mais l’intérêt majeur n’est pas là : nous allons le constater
qu’additionner deux nombres relatifs revient à additionner leurs représentations en ne gardant que les n bits de
poids faible.
Avant de le justifier, illustrons ce résultat en calculant la somme de −91 et de 113 avec un codage sur 8 bits (on
a 28 = 256 ; les nombres représentables sont compris entre −128 et 127).
−91 est négatif donc il est représenté par son complément à deux 256 − 91 = 165 = (10100101)2 .
113 est positif donc il est représenté par sa décomposition en base 2 : 113 = (01110001)2 .
On additionne ces deux représentations :
1 0 1 0 0 1 0 1
+ 0 1 1 1 0 0 0 1
1 0 0 0 1 0 1 1 0
On ne garde que les 8 derniers bits, à savoir 00010110 ; le premier bit est un 0 donc le résultat de la somme est
positif ; il représente donc l’entier (10110)2 = 22, qui est bien le résultat de l’addition de −91 avec 113.
Justifions maintenant cette méthode en envisageant quatre cas lorsqu’on additionne deux entiers relatifs a et b.
Nous allons supposer que a + b est représentable en machine, c’est-à-dire que a + b ∈ ~−2n−1 , 2n−1 − 1) :
– si a > 0 et b > 0 on calcule (a + b) ;
– si a > 0 et b < 0 on calcule a + (2n + b) = (a + b) + 2n ;
– si a + b > 0 ce nombre s’écrit sur n + 1 bit donc est tronqué : on obtient la représentation de a + b ;
– si a + b < 0 ce nombre n’est pas tronqué : on obtient la représentation de a + b ;
– si a < 0 et b > 0 la situation est identique au cas précédent ;
– si a < 0 et b < 0 on calcule (2n + a) + (2n + b) = 2n + (2n + a + b) ; ce nombre s’écrit sur n + 1 bits donc est
tronqué : il reste 2n + a + b qui est la représentation de a + b.
Soustraction de deux entiers relatifs
Calculer la soustraction a − b ça n’est jamais qu’additionner a et −b ; s’il est possible de calculer facilement
l’opposé avec la représentation en complément à deux nous pourrons ramener toute soustraction à une addition.
Considérons donc un entier x et considérons une fois de plus deux cas :
Jean-Pierre Becirspahic
4.6 informatique commune
– si x > 0 il est représenté par l’entier naturel x et son opposé −x est représenté par l’entier naturel 2n − x.
Notons x = (0xn−2 · · · x1 x0 )2 et posons y = (1yn−2 · · · y1 y0 )2 en convenant que yi = 1 − xi . Alors x + y =
(11 · · · 11)2 = 2n − 1 donc 2n − x = y + 1.
– si x < 0 il est représenté par l’entier naturel 2n + x et son opposé −x est représenté par l’entier naturel −x.
Notons 2n +x = (1xn−2 · · · x1 x0 )2 et posons y = (0yn−2 · · · y1 y0 )2 en convenant que yi = 1−xi . Alors 2n +x +y =
(11 · · · 11)2 = 2n − 1 donc −x = y + 1.
Dans les deux cas, nous constatons que pour obtenir la représentation de l’opposé de x il suffit de procéder
ainsi :
(i) considérer la représentation de x et remplacer tous les bits égaux à 0 par des 1 et réciproquement ;
(ii) additionner 1 au résultat obtenu.
Exemples. Illustrons cette démarche avec un codage sur 8 bits.
x = 113 est représenté par 01110001 ; on a donc y = 10001110 et −x est représenté par y + 1 = 10001111.
En effet, 256 − 113 = 143 = (10001111)2 .
x = −91 est représenté par 10100101 ; on a donc y = 01011010 et −x est représenté par y + 1 = 01011011.
En effet, 91 = (1011011)2 .
Dépassement de capacité
Nous venons de voir que si un processeur alloue n bits à la représentation des entiers relatifs, les entiers
représentables appartiennent à l’intervalle ~−2n−1 , 2n−1 − 1. Or si on effectue des opérations sur ces entiers il
est fort possible que le résultat du calcul n’appartienne plus à cet intervalle.
Observons ce qui se passe lorsqu’une addition dépasse la borne supérieure en considérant l’addition de 72 et de
55 avec un codage sur 8 bits :
72 = (1001000)2 est représenté par 01001000. 59 = (111011)2 est représenté par 00111011.
Effectuons l’addition de ces deux représentations :
0 1 0 0 1 0 0 0
+ 0 0 1 1 1 0 1 1
1 0 0 0 0 0 1 1
On obtient le nombre négatif représenté par 10000011 c’est à dire −125, alors que 72 + 59 = 131. Ce n’est pas
anormal puisque sur 8 bits les seuls entiers relatifs représentables sont compris entre −128 et +127, ce qui n’est
pas le cas de 131.
On peut observer que le résultat obtenu, −125, est égal à 131 − 256 ; ce n’est pas un hasard et ceci résulte de la
propriété suivante :
Théorème. — Dans une représentation en complément à deux sur n bits, le successeur de 2n−1 − 1 est égal à −2n−1 .
Preuve. Ceci est immédiat si on se souvient que 2n−1 − 1 est représenté par 0111......1111 et −2n−1 par
1000......0000.
Autrement dit, les entiers relatifs représentés par complémentation à deux sont pourvus d’une structure
cyclique, que l’on peut représenter par le schéma ci-dessous, les flèches désignant le successeur d’un entier :
−1
1
···
···
−2n−1
2n−1 − 1
Représentation des nombres 4.7
Certains langages de programmation déterminent un éventuel dépassement de capacité mais d’autres, pour des
raisons d’efficacité 4 , ne le font pas. C’est par exemple les cas du langage Caml utilisé par l’option informatique :
# max_int ;;
− : i n t = 4611686018427387903
# min_int ;;
− : i n t = −4611686018427387904
# max_int + 1 ;;
− : i n t = −4611686018427387904
En Caml les entiers sont codés en complément à deux sur 63 bits. Nous avons :
262 − 1 = 4611686018427387903 et − 262 = −4611686018427387904.
Entiers de taille arbitraire
Quant est-il de python ? Pour y répondre, rien de plus simple, il suffit de regarder quel est le successeur du plus
grand entier représentable en complément à deux sur 64 bits :
In [2]: x = 0x7fffffffffffffff
In [3]: x
Out[3]: 9223372036854775807
In [4]: x + 1
Out[4]: 9223372036854775808
Que s’est-il passé ? Contrairement au langage Caml, l’interprète Python détecte le débordement de capacité :
lorsque le résultat d’un calcul dépasse le plus grand entier représentable en complément à deux (à savoir 263 − 1)
la représentation des entiers change pour adopter la représentation sous forme dite des entiers longs.
Contrairement au complément à deux, cette représentation n’est pas limitée en taille. Cela présente bien
évidemment des avantages, mais aussi des inconvénients : les opérations sur les entiers longs prennent plus de
temps que sur les entiers classiques. Nous n’aborderons pas le problème de la représentation interne des entiers
longs, plus complexe que la représentation par complément à deux.
3. Codification des nombres décimaux
3.1 Les nombres flottants
x
Rappelons qu’un nombre décimal est un rationnel qui peut s’écrire sous la forme où x est un entier relatif.
10n
Si on considère la représentation en base 10 de l’entier x = ±(ap ap−1 · · · a1 a0 )10 , on obtient l’écriture décimale de
x x
n
en plaçant une virgule séparant les n chiffres les plus à droite des autres : n = ±(ap · · · an , an−1 · · · a0 )10 .
10 10
25 625
Par exemple, est un nombre décimal car il peut aussi s’écrire , et son écriture décimale est 6, 25.
4 100
x
De la même façon, un nombre dyadique est un rationnel qui peut s’écrire sous la forme n où x est un entier
2
relatif, et son développement dyadique s’obtient en plaçant une virgule séparant les n chiffres les plus à droite
des autres.
25
Par exemple, est un nombre dyadique et son développement dyadique est (110, 01)2 . En effet,
4
25 1 1
25 = (11001)2 et (110, 01)2 = 1 × 22 + 1 × 21 + 0 × 20 + 0 × 1 + 1 × 2 = 6, 25.
4 2 2
On s’en doute, contrairement aux humains les ordinateurs ne vont pas manipuler des nombres décimaux mais
des nombres dyadiques. Ce n’est pas un problème quand il s’agit d’entiers puisque dans ce cas la conversion
4. C’est le cas en général des langages compilés.
Jean-Pierre Becirspahic
4.8 informatique commune
binaire/décimal est exacte, mais cela pose un problème pour les nombres décimaux car le développement dyadique
d’un nombre décimal peut être infini 5 .
Par exemple, le nombre 0, 1 a une représentation décimale finie et une représentation dyadique infinie :
0, 1 = (0, 000110011001100110011001100110011001100110011 · · · )2
Ainsi, sa représentation machine sera nécessairement tronquée et ne correspondra pas exactement au nombre
0, 1 (mais en sera néanmoins très proche).
La conversion d’un nombre décimal en nombre dyadique va donc souvent provoquer une approximation qui
dans certains cas conduit à des résultats qui peuvent paraître étranges à un utilisateur non averti. Par exemple :
In [1]: 0.1 + 0.2
Out[1]: 0.30000000000000004
In [2]: (0.1 + 0.2) − 0.3
Out[2]: 5.551115123125783e−17
De ceci il faudra retenir qu’un calcul sur des nombres décimaux sera toujours entaché d’une certaine marge
d’erreur dont il faudra tenir compte, avec une conséquence importante :
L’égalité entre nombres flottants n’a pour ainsi dire aucun sens.
In [3]: 0.1 + 0.2 == 0.3
Out[3]: False
Quand on utilise le type float, il est rarissime (hormis dans un but pédagogique) d’utiliser l’égalité de valeur == ;
on fera toujours intervenir une marge d’erreur (absolue ou relative).
In [4]: abs(0.1 + 0.2 − 0.3) <= 1e−10
Out[4]: True
(On a choisi ici une marge d’erreur absolue en considérant que toute quantité inférieure ou égale à 10−10 est
nulle.)
La norme IEEE-754
Cette norme est actuellement le standard pour la représentation des nombres à virgule flottante en binaire.
Nous allons en donner une description (incomplète) pour une architecture 64 bits.
Un nombre dyadique non nul possède une représentation normalisée de la forme ±(1, b1 · · · bk )2 × 2e , où e est
un entier relatif. Par exemple, 6, 25 = (110, 01)2 a pour représentation normalisée (1, 1001)2 × 22 et −0, 375 =
−(0, 011)2 la représentation normalisée −(1, 1)2 × 2−2 .
La suite de bits b1 · · · bk est appelée la mantisse du nombre, et la puissance de 2, l’exposant.
Dans cette norme, les nombres dyadiques sont codés sur 64 bits en réservant :
– 1 bit pour le signe ;
– 11 bits pour l’exposant ;
– 52 bits pour la mantisse.
s e m
11 bits 52 bits
L’exposant est un entier relatif, mais pour permettre une comparaison plus aisée des nombres flottants, il n’est
pas codé suivant la technique de complément à deux mais suivant la technique du décalage : l’exposant e est
représenté en machine par l’entier positif e0 = e + 210 − 1.
Un entier naturel codé sur 11 bits est compris entre 0 et 211 − 1 donc a priori :
0 6 e0 6 211 − 1 ⇐⇒ 1 − 210 6 e 6 210 soit − 1023 6 e 6 1024.
Cependant, les valeurs extrêmes (e0 = 0 et e0 = 211 − 1) sont réservées à la représentation de certaines valeurs
particulières, comme nous allons le voir maintenant.
5. En revanche, la conversion réciproque ne pose pas de problème, puisque tout nombre dyadique est aussi décimal.
Représentation des nombres 4.9
– si (00000000001)2 6 e0 6 (11111111110)2 , autrement dit si 1 6 e0 6 211 − 2, le nombre est représenté sous sa
forme normalisée standard. Nous avons alors −1022 6 e 6 1023 ; ainsi le plus petit nombre strictement positif
représentable sous forme normalisée est :
(1, 0000 · · · 0000)2 × 2−1022 = 2−1022 ≈ 2, 23 × 10−308
| {z }
52 fois 0
et le plus grand :
(1, 1111 · · · 1111)2 × 21023 = 21024 − 2971 ≈ 1, 80 × 10308
| {z }
52 fois 1
– si e0 = (00000000000)2 , autrement dit si e = 1 − 210 = −1023, le nombre est représenté sous la forme dénormali-
sée ±(0, b1 · · · bk )2 × 2e . Cette optimisation permet une meilleure représentation des nombres au voisinage de 0
(mais nous ne rentrerons pas dans ces détails, très techniques). Le plus petit nombre dénormalisé strictement
positif est :
(0, 0000 · · · 0000 1)2 × 2−1023 = 2−1074 ≈ 4, 94 × 10−324
| {z }
51 fois 0
et le plus grand :
(0, 1111 · · · 1111)2 × 2−1022 = 2−1022 − 2−1074 ≈ 2, 23 × 10−308
| {z }
52 fois 1
Parmi ces nombres dénormalisés figure 0, représenté par une mantisse nulle (et un bit de signe quelconque).
– enfin, e0 = (11111111111)2 permet de coder des valeurs particulières : l’infini qu’on obtient lorsqu’un calcul
dépasse le plus grand nombre représentable (mantisse nulle), et le NaN ("Not a Number") qu’on obtient
comme résultat d’une opération invalide (mantisse non nulle).
In [1]: 2e308 # dépasse le plus grand nombre représentable
Out[1]: inf
In [2]: sqrt(−1) # calcul invalide
Out[2]: nan
Remarque. Ces deux valeurs et en particulier le nombre flottant inf, supportent les opérations et comparaisons
usuelles :
∀x ∈ R, x + inf = inf et ∀x ∈ R, x < inf
Leur usage peut permettre de simplifier certains algorithmes ; nous aurons l’occasion d’en reparler.
In [3]: a = float('inf') # le moyen le plus simple pour définir inf
In [4]: a + 5
Out[4]: inf
In [5]: a * (−2)
Out[5]: −inf
In [6]: a − a
Out[6]: nan
In [7]: a < 100000000000000000000000000000000000000
Out[7]: False
Exercice 3 Dans le type float16 de Numpy les nombres flottants sont représentés sur 16 bits : 1 bit pour le
signe, 5 bits pour l’exposant, 10 bits pour la mantisse.
Donner la représentation machine dans ce type de 1, de −2, puis du plus petit nombre strictement supérieur à 1
(ainsi que sa valeur).
Donner les représentations machine et la valeur des plus petits et plus grands nombres positifs normalisés.
Déterminer quel nombre est représenté par : 0|01110|1001001000.
Jean-Pierre Becirspahic
4.10 informatique commune
3.2 Calculs sur les nombres flottants
La représentation interne des nombres flottants a une incidence sur les opérations que l’on est susceptible de
demander à l’ordinateur, et provoque quelques désagréments dont il faut avoir conscience. Sans prétendre à
l’exhaustivité, nous allons passer en revue quelques-unes des conséquences de cette représentation.
Erreurs d’arrondis dues aux changement de bases
Nous l’avons déjà constaté, la conversion d’un nombre décimal en nombre dyadique et réciproquement provoque
des erreurs d’arrondis qui ne sont pas sans conséquences :
In [5]: (0.1 + 0.2) − 0.3
Out[5]: 5.551115123125783e−17
Aucun des trois nombres 0, 1, 0, 2 et 0, 3 n’est un nombre dyadique, donc leurs représentations machine n’est
qu’approximative.
Évidemment, un calcul isolé ne produira pas d’erreur importante, mais il peut en être autrement lorsqu’une
longue suite de calculs provoque un cumul des erreurs d’arrondi (le fameux « effet papillon » dans l’étude de
phénomènes chaotiques).
Avant de poursuivre, nous allons tenter de décortiquer le calcul ci-dessus.
Lors de la conversion décimal −→ dyadique la règle est d’arrondir au plus proche 6 . Par exemple, la repré-
sentation dyadique de 0, 1 est infinie : 0, 1 = (0, 00011001 · · · )2 (le motif 1001 se répète infiniment) donc la
représentation machine de 0, 1 est :
1, 1001 1001 · · · 1001 1010 × 2−4
| {z }
48 bits
Sans surprise, la représentation machine de 0, 2 est le double de celle-ci, à savoir :
1, 1001 1001 · · · 1001 1010 × 2−3
| {z }
48 bits
Pour additionner ces deux quantités, la plus petite des deux voit ses bits décalés d’un cran : 0, 1 est provisoire-
ment représenté par 0, 1 1001 1001 · · · 1001 101 × 2−3 = 0, 1100 1100 · · · 1100 1101 × 2−3 .
| {z }
48 bits
On réalise l’addition de ces deux nombres dyadiques pour obtenir :
10, 0110 0110 · · · 0110 0111 × 2−3
| {z }
48 bits
Ce résultat est enfin normalisé (donc arrondi au plus proche), ce qui donne : 1, 0011 0011 · · · 0011 0100 × 2−2 .
| {z }
48 bits
Sachant que 0, 3 possède la représentation dyadique infinie : (0, 010011 · · · )2 sa représentation machine est
1, 0011 0011 · · · 0011 0011 × 2−2 , ce qui explique que le calcul 0.1 + 0.2 − 0.3 ne donne pas 0 mais le nombre
| {z }
48 bits
0, 0000 0000 · · · 0000 0001 × 2−2 = 2−54 . Vérifions-le :
| {z }
48 bits
In [6]: 2**(−54)
Out[6]: 5.551115123125783e−17
C’est bien cela !
Absorption
En mathématique, l’addition est associative : (x + y) + z = x + (y + z) ; ce n’est pas le cas pour l’addition entre
nombres flottants :
In [7]: (1. + 2.**53) − 2.**53
Out[7]: 0.0
6. Plus précisément, la règle est : round to nearest, ties to even. En cas d’égalité, le nombre pair le plus proche est choisi.
Représentation des nombres 4.11
Le résultat de ce calcul est facile à comprendre. L’écriture normalisée de 1 + 253 est égale à :
(1, 0000 · · · 0000 1)2 × 253
| {z }
52 fois 0
mais comme la mantisse n’occupe que 52 bits, le résultat de cette addition n’est pas représentable en machine et
sera approché par :
(1, 0000 · · · 0000)2 × 253
| {z }
52 fois 0
53 53
Autrement dit, en arithmétique flottante, 1 + 2 = 2 . Ce mécanisme porte le nom d’absorption : l’addition de
deux quantités dont l’écart relatif est très important entraîne l’absorption de la plus petite de ces deux quantités par la
plus grande.
En conséquence, il est parfois nécessaire de réorganiser les calculs pour obtenir un résultat plus conforme à nos
attentes :
In [8]: 1. + (2.**53 − 2.**53)
Out[8]: 1.0
Cancellation
Un autre problème se présente lors de la soustraction de deux quantités très proches. Pour fixer les idées,
supposons que l’on connaisse deux quantités voisines x et y avec une précision de 20 chiffres significatifs après
la virgule :
x = 1,10010010000111111011
y = 1,10010010000111100110
x−y = 0,00000000000000010101
le résultat x − y sera normalisé pour être représenté en machine par (1, 0101)2 × 2−16 ; autrement dit, la précision
ne sera plus que de 4 chiffres après la virgule. Cette perte drastique de précision porte le nom de cancellation,
car la différence de deux quantités très voisines fait littéralement s’évanouir les chiffres significatifs.
Prendre conscience de ce phénomène permet de conditionner un calcul pour le rendre moins sensible à
l’évanescence des chiffres significatifs. Par exemple, pour un calcul numérique il est préférable de calculer
1 1 1
plutôt que − car, bien que ces deux quantités soient mathématiquement égales, la seconde est
x(x + 1) x x+1
beaucoup plus sensible au phénomène de cancellation.
En règle générale, lors d’un calcul numérique il faut suivre les recommandations suivantes :
– on évite d’additionner deux quantités dont l’écart relatif est très grand ;
– on évite de soustraire deux quantités très voisines.
Et pour finir. . .
Il ne faudrait surtout pas croire que ces petites erreurs de calcul et d’arrondi soient négligeables, et l’anecdote
suivante devrait vous convaincre de l’importance qu’il y a à en prendre conscience.
Le 25 février 1991, à Dharan en Arabie Saoudite, un missile Patriot américain a raté l’interception d’un missile
Scud irakien, ce dernier provoquant la mort de 28 personnes. La commission d’enquête chargée de comprendre
la raison de cet échec a mis en évidence le défaut suivant :
L’horloge interne du missile Patriot mesure le temps en 1/10s. Pour obtenir le temps en seconde, le système
multiplie ce nombre par 10 en utilisant un registre de 24 bits en virgule fixe. Or 1/10 n’est pas un nombre
dyadique donc a été arrondi : le registre de 24 bits contient (0, 00011001100110011001100)2 et induit une erreur
binaire de (0, 0000000000000000000000011001100 . . . )2 , soit approximativement 0, 000000095 en notation
décimale.
En multipliant cette quantité par le nombre de 1/10s pendant 100h (le temps écoulé entre la mise en marche
du système et le lancement du missile Patriot), on obtient le décalage entre l’horloge interne de missile et le
temps réel, soit :
0, 000000095 × 100 × 3600 × 10 ≈ 0, 34s.
Or un missile Scud vole à la vitesse approximative de 1, 676m/s donc parcourt plus de 500m en 0, 34s, ce qui le
fait largement sortir de la zone d’acquisition de sa cible par le missile d’interception 7 .
7. Référence : http://ta.twi.tudelft.nl/nw/users/vuik/wi211/disasters.html.
Jean-Pierre Becirspahic