Adloun

Représentation des nombres en machine

Cours · Python (école d'ingénieur), chapitre 3 · école d'ingénieur, informatique · le début du chapitre, la suite sur Adloun

3.1 Introduction et Motivation

Dans ce chapitre, nous allons nous intéresser à la façon dont un nombre (qu'il soit entier ou réel) est représenté en mémoire à l'intérieur d'un ordinateur. La mémoire des ordinateurs est découpée en blocs élémentaires de 8 bits (un octet) et les processeurs modernes de 64 bits travaillent généralement avec des registres de 8 octets (soit 64 bits).

Disposant d'un nombre fini de bits pour représenter un ensemble infini de nombres mathématiques, il est indispensable de comprendre comment les nombres sont codés en base 2 pour anticiper les limites physiques du matériel (dépassements de capacité) et les erreurs d'arrondis qui peuvent fausser des simulations numériques sensibles.

3.2 Représentation dans une Base

Nous utilisons naturellement l'écriture décimale (base 10) depuis l'enfance. Plus généralement, pour tout entier base , on peut définir la représentation d'un entier en convenant que l'écriture positionnelle représente le nombre :

Pour assurer l'unicité de l'écriture, on impose pour chaque chiffre la contrainte et .

3.2.1 Écriture en Base 2

En base 2 (système binaire), seuls les chiffres 0 et 1 sont autorisés. Les opérations arithmétiques élémentaires s'y effectuent de façon similaire à la base 10 :

10111
+1101
100100
1010
101
1010
1010
110010

3.2.2 Algorithmes de Changement de Base

Pour convertir une chaîne représentant un nombre écrit en base vers son équivalent entier en base 10, nous pouvons implémenter la formule directe ou appliquer de manière plus efficace le schéma de Horner :


# Algorithme direct avec calculs de puissances
def base10_direct(x, b=2):
    s = 0
    k = len(x) - 1
    for a in x:
        s += int(a) * (b ** k)
        k -= 1
    return s

# Algorithme de Horner (sans puissance, O(n) multiplications)
def base10_horner(x, b=2):
    u = 0
    for a in x:
        u = b * u + int(a)
    return u

Pour l'opération inverse (conversion d'un entier décimal vers sa représentation en base ), on effectue des divisions euclidiennes successives par la base :


def baseb(x, b=2):
    s = ''
    y = x
    while y > 0:
        s = str(y % b) + s
        y //= b
    return s if s != '' else '0'

3.2.3 Cas Particuliers : Base 2 et Base 16

En Python, il est possible de saisir directement des littéraux dans d'autres bases :

Comme , un chiffre hexadécimal correspond exactement à un groupe de 4 bits. Les fonctions intégrées bin(x) et hex(x) réalisent ces conversions vers des chaînes de caractères formatées.

3.3 Codification des Nombres Entiers

Les ordinateurs manipulent des types d'entiers à taille fixe au niveau du processeur.

3.3.1 Les Entiers Naturels

Sur bits, on peut coder les entiers de l'intervalle . Pour bits, cela permet d'adresser des cellules mémoire allant de à .

3.3.2 Les Entiers Relatifs et le Complément à Deux

Pour coder les entiers relatifs, on doit pouvoir représenter le signe. La solution standard est le codage en complément à deux.

Définition 3.1Complément à deux

Dans un codage sur bits, un entier relatif est représenté en machine par la valeur binaire non signée de :

Le premier bit (le bit de poids fort) sert naturellement de bit de signe (0 pour positif, 1 pour négatif). Pour obtenir l'opposé d'un nombre sous cette codification, il existe un algorithme matériel très simple :

3.3.3 Dépassement de Capacité et Cyclicité

L'addition binaire en complément à deux présente l'immense avantage que l'unité arithmétique et logique (ALU) traite les nombres signés exactement comme des nombres non signés, en tronquant simplement le résultat aux bits de poids faible.

Toutefois, si le résultat d'une opération sort de l'intervalle , un dépassement de capacité (overflow) survient : la structure est cyclique. Le successeur du plus grand entier positif est le plus petit entier négatif .

iRemarqueEntiers Python de taille arbitraire

Alors que des langages système (comme le C) ou d'autres interprétés (comme Caml) subissent ce dépassement, Python 3 gère automatiquement les entiers en précision arbitraire (via le type int long). Si un entier dépasse , Python bascule sa représentation interne de manière transparente sans aucune perte de bits.

3.4 Codification des Nombres Réels (Flottants)

Un nombre possédant une écriture décimale finie est un nombre décimal (de la forme ). De même, un nombre représentable par une écriture binaire finie est un nombre dyadique.

Définition 3.2Nombre dyadique

Un nombre dyadique est un rationnel pouvant s'écrire sous la forme où et .

3.4.1 Le problème de la conversion binaire

La plupart des nombres décimaux (tels que ou ) ne sont pas des nombres dyadiques. Leur développement en base 2 est infini et périodique :

Par conséquent, lorsqu'on saisit 0.1 dans un programme, l'ordinateur le tronque pour le stocker, introduisant une erreur d'arrondi dès le départ.

Exemple 3.3Erreur d'arrondi classique

En Python, l'expression 0.1 + 0.2 == 0.3 renvoie False car :


>>> 0.1 + 0.2
0.30000000000000004

Pour tester l'égalité de nombres réels en programmation scientifique, on doit toujours utiliser une tolérance (ou seuil de précision ) :


# Utilisation d'une marge absolue
if abs((0.1 + 0.2) - 0.3) <= 1e-10:
    print("Égalité approchée")

3.4.2 La Norme IEEE-754 (Double Précision 64 bits)

Les nombres à virgule flottante sont codés en standard IEEE-754 double précision sur 64 bits selon la répartition suivante :

Le nombre réel représenté sous forme normalisée est :

avec .

Les exposants extrêmes ont des significations particulières :

3.5 Calculs Flottants et Phénomènes Numériques

L'arithmétique flottante induit des comportements non intuitifs qui exigent une vigilance particulière.

3.5.1 L'Absorption Numérique

L'addition de deux nombres flottants d'ordres de grandeur très différents provoque la perte des chiffres significatifs du plus petit nombre. C'est l'absorption. En précision double, la mantisse ne codant que 52 bits (soit environ 15 à 17 chiffres décimaux significatifs), l'opération suivante :


>>> (1.0 + 2.0**53) - 2.0**53
0.0

renvoie 0.0 car la valeur 1.0 est totalement absorbée lors de l'addition. Pour corriger cela, il faut réorganiser les calculs :


>>> 1.0 + (2.0**53 - 2.0**53)
1.0

3.5.2 La Cancellation (Perte de Précision par Soustraction)

Lorsqu'on soustrait deux nombres très proches, les chiffres significatifs communs s'annulent, et le résultat est dominé par des erreurs d'arrondis initiales. C'est la cancellation.

Exemple 3.4Réorganisation algébrique

Pour calculer pour de très grandes valeurs de , la formule algébriquement équivalente est numériquement beaucoup plus stable car elle évite la soustraction de deux nombres extrêmement proches.

3.5.3 Étude de Cas : La Catastrophe du Missile Patriot (1991)

Le 25 février 1991, une batterie de missiles Patriot américaine a manqué l'interception d'un missile Scud irakien à Dharan, causant la mort de 28 soldats. La commission d'enquête a identifié une simple erreur d'arrondi dans le logiciel de calcul de temps :

La suite du cours est sur Adloun

Le chapitre en entier, formules et figures comprises, avec le tuteur, les fiches, les QCM et les exercices.

Lire la suite du cours

En accès libre, sans inscription.

Exercices corrigés de ce chapitre

Lire la suite du cours →