Entiers relatifs et complément à deux
Introduction
Vous savez désormais représenter un entier positif en binaire. Mais comment fait-on pour coder un nombre négatif comme ? Il n'y a pas de signe moins disponible — seuls les bits 0 et 1 existent. Plusieurs solutions ont été imaginées au fil de l'histoire de l'informatique, mais une seule s'est imposée universellement : le complément à deux.
Ce codage a une propriété quasi magique : il permet d'additionner un positif et un négatif comme s'il s'agissait de deux entiers ordinaires, sans distinction de signe. Les circuits arithmétiques s'en trouvent considérablement simplifiés.
Le bit de signe — première idée
L'idée la plus naïve consiste à réserver le bit de poids fort comme bit de
signe : 0 pour le positif, 1 pour le négatif. Les autres bits codent la
valeur absolue.
Sur 4 bits :
0101représenterait .1101représenterait .
Cette approche, appelée signe et valeur absolue, semble naturelle… mais elle souffre de défauts rédhibitoires :
- Il existe deux zéros :
0000et1000, ce qui complique les comparaisons. - L'addition d'un positif et d'un négatif ne fonctionne plus de façon élémentaire : il faut un circuit dédié pour gérer le signe.
Le complément à deux
Sur bits, le complément à deux d'un entier se définit ainsi : on
écrit comme un entier non signé, on inverse tous les bits (les 0
deviennent 1 et inversement), puis on ajoute 1.
Exemple sur 8 bits : représenter .
| Étape | Valeur |
|---|---|
| 1. Écrire sur 8 bits | 0000 0101 |
| 2. Inverser tous les bits | 1111 1010 |
| 3. Ajouter 1 | 1111 1011 |
Donc en complément à deux sur 8 bits.
0 pour les positifs,
1 pour les négatifs. Mais contrairement au codage signe/valeur absolue, les
autres bits ne sont pas la valeur absolue — ils résultent de la double
opération « inversion + 1 ».Pourquoi ça marche — l'addition modulo
Sur bits, l'arithmétique se fait modulo . Coder un entier négatif revient à utiliser la valeur positive qui, modulo , se comporte exactement comme .
Vérification : sur 8 bits, correspond à . C'est bien le résultat trouvé plus haut.
Et l'addition fonctionne :
Le 9ᵉ bit déborde de l'octet et est ignoré : il reste . L'opération donne bien zéro.
a << n décale les bits de a de n rangs vers la gauche (équivaut à
multiplier par ) ; a & b réalise un ET bit-à-bit (chaque bit du
résultat vaut 1 seulement si les deux bits correspondants de a et b
valent 1). Combinés, x & ((1 << n) - 1) ne garde que les n bits de poids
faible de x.Sortie attendue :
-5 sur 8 bits : 0b11111011 (251 non signé)
Étendue représentable
Sur bits en complément à deux, on couvre l'intervalle :
| Bits | Minimum | Maximum |
|---|---|---|
| 8 | ||
| 16 | ||
| 32 | ||
| 64 |
On remarque que l'intervalle n'est pas symétrique : il y a un entier négatif de plus que d'entiers positifs. C'est la conséquence du fait qu'il n'existe qu'un seul zéro (qui est compté comme positif au sens large).
Sur 8 bits en complément à deux, combien d'entiers distincts peut-on représenter ?
Bits nécessaires pour une somme ou un produit
Combien de bits faut-il prévoir si l'on additionne ou si l'on multiplie deux entiers ?
- Somme de deux entiers de bits : il peut y avoir une retenue qui ajoute 1 bit. Le résultat tient sur bits.
- Produit de deux entiers de bits : le résultat peut atteindre , donc il faut prévoir bits.
Le cas Python — entiers de taille arbitraire
Python (à partir de la version 3) ne limite pas la taille des entiers : ils peuvent croître autant que la mémoire le permet.
print(2 ** 100)
# 1267650600228229401496703205376
print(-1 << 8)
# -256
print(bin(-5))
# -0b101 — Python affiche le signe, pas le complément à deux
Cette commodité a un coût : un entier Python n'a pas une taille fixe en mémoire, donc les opérations sont un peu plus lentes que sur un entier 32 ou 64 bits d'un langage bas niveau comme C.
Que vaut −1-1−1 en complément à deux sur 8 bits ?
Pièges courants
- Le bit de poids fort vaut 1 pour tout négatif, mais ce n'est pas l'opposé en valeur absolue : il faut vraiment refaire « inverser + 1 ».
- Sur un type entier de taille fixe (C, Java), additionner deux grands positifs peut donner un négatif à cause du débordement (overflow). Python vous protège de cela.
- Pour passer de la représentation interne (positive) au nombre signé, on utilise toujours la convention : si le bit de poids fort est 1, on soustrait .
Pour aller plus loin
Le complément à deux explique aussi pourquoi le décalage à droite d'un nombre négatif est subtil : il faut propager le bit de signe pour préserver la sémantique de division entière. Vous croiserez cette opération en bas niveau (C, assembleur) ou en manipulation de bits.