← Vizion.Blog / Licence / Architecture des ordinateurs

Cours rédigé par Claude, une IA d'Anthropic, pour mes révisions de L1, à partir de mes notes de cours : il s'adresse donc à moi (« tes notes », « ta prof »). Dans cette version publique, j'ai retiré ma feuille d'exercices, qui est peut-être notée.

Architecture des ordinateurs, séances du 21 et du 28 septembre

Le binaire, depuis le tout débutBits, bases, conversions, nombres négatifs, et les calculs que fait vraiment le processeur.

Tes notes contiennent presque tout le programme des deux premières séances, mais dans l'ordre où le cours s'est déroulé, avec des essais, des ratures et quelques erreurs. On reprend tout dans l'ordre logique : ce qu'est un bit, comment on écrit un nombre dans n'importe quelle base, comment on calcule, et surtout comment une machine qui ne connaît que 0 et 1 représente un nombre négatif. Six laboratoires te laissent manipuler les bits toi-même.

0Mode d'emploi

Comment travailler

  • Lis dans l'ordre. Les sections 1 à 5 couvrent la séance du 21 septembre, les sections 6 à 8 celle du 28.
  • Garde une feuille à côté. Pose chaque calcul à la main avant de regarder le mien ou d'utiliser un laboratoire.
  • Réponds aux questions « Vérifie-toi » avant de cliquer. Une erreur ici ne coûte rien.
  • Vérifie toujours en décimal. Un calcul binaire se contrôle en vingt secondes en repassant en base 10. C'est ton test unitaire.
  • Rythme conseillé : sections 1 à 4 à une première séance, 5 à 7 à une deuxième (la 7 est la plus importante), puis 8 et 9.

Où ça se place dans le semestre

D'après le plan de ta prof, le 5 octobre est consacré aux portes logiques, et le 19 octobre tu construiras dans Logisim un additionneur capable de soustraire. Cet additionneur calcule exactement comme dans la section 7 de cette page : les deux cours vont ensemble. Les portes logiques ont leur propre page : Les portes logiques, depuis le tout début.

Les mots, et où ils sont expliqués

1Le bit, l'octet, le mot

1.1 Le bit

Un processeur contient des milliards de minuscules interrupteurs électroniques, les transistors. Chacun est dans un état parmi deux. Tes notes le disent ainsi : 0, le courant ne passe pas ; 1, le courant passe. En réalité, on mesure plutôt une tension : basse pour 0, haute pour 1. L'idée reste la même : deux états, faciles à distinguer.

Un chiffre qui ne peut valoir que 0 ou 1 s'appelle un bit (binary digit, chiffre binaire). C'est la plus petite quantité d'information possible : la réponse à une question par oui ou par non.

Pourquoi deux états, et pas dix ?

Distinguer dix niveaux de tension serait fragile : avec un peu de bruit électrique, un 6 deviendrait un 7. Avec deux niveaux très éloignés, l'erreur est presque impossible. C'est le principe d'un interrupteur de lumière : il n'a pas de position « à moitié allumé ».

1.2 Octet et mot

NomTailleExemple, usage
bit (bit)1 bit0 ou 1
quartet (nibble)4 bits1011 ; exactement un chiffre hexadécimal (section 3.4)
octet (byte)8 bits1010 0111 ; la plus petite case de la mémoire qui a sa propre adresse
mot (word)16, 32 ou 64 bits selon la machinela taille des nombres que le processeur traite en une seule opération

Ton i5-1135G7 est un processeur 64 bits : ses registres de calcul contiennent des mots de 64 bits. En français, un octet fait toujours 8 bits, alors que la taille d'un mot dépend de la machine. C'est pourquoi on précise toujours le nombre de bits.

Attention aux outils x86 (débogueurs, désassembleurs) : par tradition, WORD y désigne 16 bits, DWORD 32 bits et QWORD 64 bits, quel que soit le processeur.

On écrit les bits par paquets de 4 pour les lire plus facilement, comme on écrit 65 536 avec une espace : 1010 0111 plutôt que 10100111.

1.3 Combien de valeurs avec n bits ?

Avec 1 bit, on écrit 2 valeurs : 0 et 1. Avec 2 bits, 4 valeurs : 00, 01, 10, 11. Chaque bit ajouté double le nombre de possibilités : on reprend toutes les combinaisons précédentes, une fois avec un 0 devant, une fois avec un 1 devant.

À retenir

Avec n bits, on peut écrire 2n combinaisons différentes. Utilisées pour des entiers positifs, elles vont de 0 à 2n − 1.

Pourquoi « − 1 » ? Parce qu'on commence à 0. Avec 3 chiffres décimaux, il y a 10³ = 1 000 combinaisons, de 000 à 999 : la plus grande vaut 1 000 − 1. Pour l'octet, 2⁸ = 256 combinaisons, de 0 à 255 : c'est le « de 0 à 255 » de tes notes.

n bits2n valeursentiers de 0 àoù on le rencontre
121vrai ou faux
41615un chiffre hexadécimal
8256255un octet ; une composante de couleur en CSS
101 0241 0232¹⁰ = 1 024 ≈ 1 000 : d'où le préfixe « kibi » (Ki)
1665 53665 535un numéro de port TCP ou UDP
324 294 967 2964 294 967 295une adresse IPv4
64≈ 1,8 × 10¹⁹≈ 1,8 × 10¹⁹un registre de ton processeur

Les puissances de 2 de ton cours sont à connaître par cœur, au moins jusqu'à 2¹⁰ : 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1 024.

La question inverse : combien de bits faut-il ?

Elle revient sans cesse en TD. Pour représenter N valeurs différentes, il faut le plus petit n tel que 2n ≥ N.

Le piège du « + 1 »

Pour écrire les entiers de 0 à N, il y a N + 1 valeurs. Pour aller jusqu'à 256 inclus, 8 bits ne suffisent pas : 256 s'écrit 1 0000 0000, avec 9 bits.

ko ou Kio ?

Un Kio (kibioctet) vaut 1 024 octets, un ko (kilooctet) vaut 1 000 octets. Les fabricants de disques comptent en puissances de 10, beaucoup de logiciels en puissances de 2. C'est pourquoi un SSD de 512 Go s'affiche autour de 477 Gio : 512 × 10⁹ / 2³⁰ ≈ 476,8.

1.4 Laboratoire : l'octet

Clique sur les bits pour les allumer ou les éteindre. Chaque bit allumé ajoute son poids, écrit au-dessus. Essaie d'écrire 167, puis 255, puis 128.

Vérifie-toi (section 1)

Combien de valeurs différentes peut-on écrire avec 6 bits ?

2⁶ = 64 valeurs, de 0 à 63.

Combien de bits faut-il au minimum pour numéroter 100 objets ?

2⁶ = 64 < 100 ≤ 128 = 2⁷. Donc 7 bits.

Quel est le plus grand entier qu'on peut écrire sur 16 bits, en non signé ?

2¹⁶ − 1 = 65 535, le plus grand numéro de port.

Un mot de 32 bits, c'est combien d'octets ?

32 / 8 = 4 octets.

2La numération positionnelle

2.1 Ce que veut dire « 2026 »

Tu as écrit cet exemple dans tes notes. On le reprend, parce que tout le reste en découle.

2026 = 2 × 10³ + 0 × 10² + 2 × 10¹ + 6 × 10⁰ =2 000 + 0 + 20 + 6

Chaque chiffre est multiplié par une puissance de 10 qui dépend de sa position. On numérote les positions à partir de la droite, en commençant à 0, comme les indices d'une liste en Python. Le même chiffre 2 vaut 2 000 en position 3, et 20 en position 1. C'est pour ça qu'on parle de numération positionnelle (positional notation). Les chiffres romains, eux, ne sont pas positionnels : dans XXX, chaque X vaut dix, quelle que soit sa place.

2.2 Changer de base

Le nombre 10 n'a rien de spécial : il vient de nos dix doigts. On peut remplacer 10 par n'importe quel entier b ≥ 2, qu'on appelle la base.

Numération en base b

On utilise b chiffres, de 0 à b − 1. Le chiffre placé en position k vaut chiffre × bk, et la valeur du nombre est la somme de ces produits :

(cn−1 … c1 c0)b = cn−1 × bn−1 + … + c1 × b + c0

C'est la formule encadrée dans tes notes : « chiffre × baseposition ».

BaseNomChiffresPoids des positions
2binaire0 11, 2, 4, 8, 16, 32…
8octal0 1 2 3 4 5 6 71, 8, 64, 512…
10décimal0 1 … 91, 10, 100, 1 000…
16hexadécimal0 … 9 A B C D E F1, 16, 256, 4 096…

En base 16, il faut seize chiffres, et nos chiffres s'arrêtent à 9. On emprunte donc des lettres : A = 10, B = 11, C = 12, D = 13, E = 14, F = 15. Chacune est un seul chiffre : dans (1A)₁₆, le A vaut 10 unités, et le nombre vaut 1 × 16 + 10 = 26.

On écrit la base en indice : (1010)₂ = (10)₁₀. Sans indice, « 10 » est ambigu : dix en décimal, deux en binaire, huit en octal, seize en hexadécimal.

Dans tes outils de tous les jours

  • En Python, les préfixes 0b, 0o et 0x indiquent la base : 0b1010 == 10. Les fonctions bin(167), oct(167), hex(167) convertissent, et int('5D7', 16) fait l'inverse. Sers-t'en pour vérifier tes calculs, jamais pour les faire à ta place.
  • En CSS, dans ton cours de site personnel, une couleur #FF8800 est trois octets écrits en hexadécimal : rouge FF = 255, vert 88 = 136, bleu 00 = 0.

2.3 Compter : le compteur kilométrique

Un compteur kilométrique a des roues numérotées de 0 à 9. Quand une roue passe de 9 à 0, elle fait avancer d'un cran la roue de gauche : c'est la retenue (carry). En base b, c'est pareil, mais chaque roue n'a que b chiffres. En binaire, une roue n'a que 0 et 1 : dès qu'elle dépasse 1, elle repasse à 0 et pousse sa voisine. C'est pourquoi 0111 + 1 = 1000 : trois roues repassent à 0 d'un coup, exactement comme 999 + 1 = 1 000.

Ton tableau de 0 à 21 se lit avec cette image. La colonne de droite alterne 0, 1, 0, 1. La suivante change tous les 2 nombres, la suivante tous les 4, puis tous les 8. Chaque colonne change deux fois moins souvent que sa voisine de droite.

2.4 Pourquoi l'octal et l'hexadécimal ?

Le binaire est long à écrire et facile à mal recopier : 1 495 s'écrit 101 1101 0111. L'octal et l'hexadécimal servent d'abréviations, parce que 8 = 2³ et 16 = 2⁴. Un chiffre octal remplace exactement 3 bits, et un chiffre hexadécimal exactement 4 bits (section 3.4). Un octet s'écrit donc toujours avec 2 chiffres hexadécimaux, de 00 à FF.

C'est pour ça que les adresses mémoire s'écrivent en hexadécimal, comme tu l'as noté. Dans un débogueur, ou sur Root-Me, tu verras des adresses comme 0x7ffd3a2c1e40.

Vérifie-toi (section 2)

Que vaut (25)₉ en décimal ?

2 × 9 + 5 × 1 = 23. C'est l'exemple de tes notes.

Que vaut (10)₁₆ en décimal ?

1 × 16 + 0 = 16. En toute base b, « 10 » désigne le nombre b lui-même.

Quel est le plus grand chiffre en base 8 ?

Les chiffres vont de 0 à b − 1, donc de 0 à 7.

Dans (1000)₂, combien vaut le 1 ?

Position 3, donc 2³ = 8.

3Les conversions

3.1 D'une base b vers la base 10

On applique la définition : chaque chiffre, multiplié par la base puissance sa position, puis on additionne.

(1011011)₂ = 64 + 16 + 8 + 2 + 1 = 91 Comme dans tes notes : on écrit les poids 64, 32, 16, 8, 4, 2, 1 au-dessus des bits, et on additionne ceux qui sont sous un 1. (247)₈ = 2 × 64 + 4 × 8 + 7 = 128 + 32 + 7 = 167 (5D7)₁₆ = 5 × 256 + 13 × 16 + 7 = 1 280 + 208 + 7 = 1 495 D vaut 13. Pense à remplacer les lettres avant de multiplier. (25)₉ = 2 × 9 + 5 = 23

La méthode du programmeur (Horner)

On lit les chiffres de gauche à droite, et on fait à chaque fois : valeur = valeur × base + chiffre. Pour (1011011)₂ : 1, puis 1 × 2 + 0 = 2, puis 2 × 2 + 1 = 5, puis 11, 22, 45, 91. Pas besoin de connaître les puissances, et c'est exactement ce que fait int('1011011', 2) :

def vers_decimal(chiffres, base):
    valeur = 0
    for c in chiffres:
        valeur = valeur * base + int(c, 16)
    return valeur

3.2 De la base 10 vers une base b : les divisions successives

On divise le nombre par b et on note le reste. On recommence avec le quotient, jusqu'à obtenir un quotient nul. Le résultat s'écrit avec les restes, lus du dernier au premier. Voici ton exemple de 72 en binaire :

divisionquotientreste
72 ÷ 2360
36 ÷ 2180
18 ÷ 290
9 ÷ 241
4 ÷ 220
2 ÷ 210
1 ÷ 201

On lit la colonne des restes de bas en haut : (72)₁₀ = (1001000)₂, soit 0100 1000 sur 8 bits. Vérification : 64 + 8 = 72.

Pourquoi lire à l'envers ? Fais la même chose en base 10 avec 2026 : 2026 ÷ 10 = 202, reste 6. Le reste, c'est le chiffre le plus à droite, et diviser par 10 décale le nombre d'un cran vers la droite. En base 2, c'est pareil : le premier reste, 72 mod 2, est le chiffre des unités (position 0). Les restes sortent donc de droite à gauche, et il faut les relire dans l'autre sens. En Python :

def vers_base(n, b):
    chiffres = ""
    while n > 0:
        chiffres = "0123456789ABCDEF"[n % b] + chiffres   # on ajoute à GAUCHE
        n = n // b
    return chiffres or "0"

La méthode marche pour toutes les bases. Ton exemple en base 8 : 167 ÷ 8 = 20 reste 7 ; 20 ÷ 8 = 2 reste 4 ; 2 ÷ 8 = 0 reste 2. Donc (167)₁₀ = (247)₈.

3.3 Méthode 2 : retirer les puissances

Imagine que tu dois payer 167 € avec des billets de 128, 64, 32, 16, 8, 4, 2 et 1 €, chaque billet au plus une fois. Tu prends le plus gros billet possible, puis le suivant, et ainsi de suite. C'est ce que tu as fait pour 167 :

128oui : il reste 167 − 128 = 39 64non (64 > 39) 32oui : il reste 39 − 32 = 7 16, 8non 4, 2, 1oui, oui, oui : 7 = 4 + 2 + 1

Les billets pris donnent les 1 : (167)₁₀ = 1010 0111. Cette méthode est la plus rapide quand tu connais tes puissances de 2. Pour les bases 8 et 16, les divisions sont plus sûres.

3.4 Binaire, octal, hexadécimal : les paquets

La méthode des paquets

Binaire vers octal : on coupe le nombre en paquets de 3 bits en partant de la droite, on complète le dernier paquet à gauche avec des zéros, et on remplace chaque paquet par son chiffre (de 0 à 7).

Binaire vers hexadécimal : pareil, avec des paquets de 4 bits.

Dans l'autre sens, on remplace chaque chiffre par son paquet de 3 ou 4 bits.

Ton exemple, 1 495 = 10111010111. Les zéros ajoutés à gauche sont en gris.

paquets de 3 bits → (2727)₈

paquets de 4 bits → (5D7)₁₆

Pourquoi ça marche ? Un paquet de 3 bits vaut de 0 à 7, exactement un chiffre octal. Et les paquets successifs, en partant de la droite, pèsent 2⁰, 2³, 2⁶, c'est-à-dire 1, 8, 64 : les puissances de 8. Le découpage ne change pas le nombre, il le relit seulement par blocs.

Toujours couper depuis la droite

Si tu coupes 10111010111 depuis la gauche (101 110 101 11), tu obtiens n'importe quoi. C'est la position 0, les unités, qui doit tomber dans le premier paquet.

Le tableau à connaître par cœur, avec les 16 paquets de 4 bits. Les 8 premiers donnent aussi les paquets de 3 bits de l'octal (on enlève le 0 de tête).

hexa0123456789ABCDEF
décimal0123456789101112131415
binaire0000000100100011010001010110011110001001101010111100110111101111

Ton deuxième exemple, 267 = 1 0000 1011 : en paquets de 3, 100 001 011 donne (413)₈ ; en paquets de 4, 0001 0000 1011 donne (10B)₁₆.

3.5 Entre octal et hexadécimal : le binaire sert de pont

8 et 16 ne sont pas des puissances l'une de l'autre, il n'y a donc pas de raccourci direct. On passe par le binaire, comme dans tes notes avec (ACDC)₁₆ :

(ACDC)₁₆ = 1010 1100 1101 1100 Chaque chiffre hexadécimal devient son paquet de 4 bits. =1 010 110 011 011 100 On recoupe en paquets de 3, toujours depuis la droite. =(126334)₈

3.6 Laboratoire : le convertisseur

Tape un nombre et choisis sa base. La page affiche ses quatre écritures, les paquets de bits, et les divisions successives vers la base de ton choix. Fais toujours la conversion à la main d'abord.

Vérifie-toi (section 3)

Que vaut (1101)₂ en décimal ?

8 + 4 + 1 = 13.

Comment s'écrit (7F)₁₆ en binaire ?

7 → 0111, F → 1111. (7F)₁₆ = 127, le plus grand entier positif d'un octet signé (section 6).

Comment s'écrit (101110)₂ en octal ?

101 | 110 → 5 et 6. Vérification : 5 × 8 + 6 = 46 = 32 + 8 + 4 + 2.

Pour convertir 1 000 en binaire par divisions, le premier reste obtenu est…

1 000 mod 2 = 0 : c'est le bit de droite, et 1 000 est pair. D'où la lecture de bas en haut.

4Additionner et soustraire, dans n'importe quelle base

4.1 L'addition binaire

On pose l'addition en colonnes, comme à l'école, en partant de la droite. Il n'y a que quatre cas à connaître :

calculrésultatj'écrisje retiens
0 + 0000
0 + 1 ou 1 + 0110
1 + 11001
1 + 1 + 1 (avec une retenue)1111

« 1 + 1 = 10 » se lit « un plus un égale deux », écrit en binaire : j'écris 0, je retiens 1. Les deux additions de tes notes, avec leurs retenues en rouge :

1111retenues
101111
+01117
1001018
111retenues
0000111014
+0000111115
0001110129

4.2 La même règle dans toutes les bases

Addition en base b

Dans chaque colonne, on additionne les deux chiffres et la retenue éventuelle. Si la somme est plus petite que b, on l'écrit. Sinon, on écrit somme − b et on retient 1.

C'est la phrase de tes notes : « si on dépasse, on écrit de combien on dépasse, et on retient 1 ».

Ton addition en octal, colonne par colonne en partant de la droite (les sommes de chaque colonne sont faites en décimal, de tête) : 2 + 4 = 6 ; 7 + 3 = 10, qui dépasse 8, donc j'écris 10 − 8 = 2 et je retiens 1 ; 6 + 3 + 1 = 10, j'écris 2, je retiens 1 ; 5 + 6 + 1 = 12, j'écris 4, je retiens 1 ; 2 + 2 + 1 = 5 ; 1 + 1 = 2.

111retenues
125672(ABBA)₁₆ = 43 962
+126334(ACDC)₁₆ = 44 252
25422688 214

Ton addition en hexadécimal : A + C = 10 + 12 = 22, j'écris 22 − 16 = 6, je retiens 1 ; B + D + 1 = 11 + 13 + 1 = 25, j'écris 9 ; B + C + 1 = 24, j'écris 8 ; A + A + 1 = 21, j'écris 5, et la dernière retenue donne un chiffre de plus.

1111retenues
ABBA43 962
+ACDC44 252
1589688 214

4.3 La soustraction : l'emprunt

Quand le chiffre du haut est trop petit, on emprunte 1 à la colonne de gauche. Ce 1 vaut une base entière dans la colonne actuelle : 10 en décimal, 2 en binaire, 8 en octal, 16 en hexadécimal. C'est ta note : « on ajoute 16 à celui qui emprunte, on retire 1 à sa voisine ».

Soustraction en base b

Si le chiffre du haut est plus petit que celui du bas, on lui ajoute b, et on retire 1 au chiffre du haut de la colonne de gauche.

Si cette colonne de gauche contient 0, elle emprunte à son tour à sa propre voisine, et le 0 traversé devient b − 1 : 9 en décimal, 1 en binaire, 7 en octal, F en hexadécimal. C'est le piège de 100 − 1 = 99.

C'est la soustraction qu'on a faite ensemble. Les « −1 » marquent les colonnes qui ont prêté 1 à leur voisine de droite. À la position 3, on emprunte à travers la position 4, qui vaut 0 et devient 1 :

−1−1−1emprunts
10101143
−00110113
01111030

Ton exemple en hexadécimal : 9 − A est impossible, donc 9 + 16 = 25 et 25 − 10 = 15 = F ; le E a prêté, il devient D, et D − 8 = 5 ; enfin 1 − 0 = 1.

−1emprunts
1E9489
−08A138
15F351

4.4 Laboratoire : poser une opération

Choisis une base et une opération, tape deux nombres, et suis le calcul colonne par colonne avec le bouton « Colonne suivante ».

Vérifie-toi (section 4)

(111)₂ + (1)₂ = ?

7 + 1 = 8. Comme 999 + 1 = 1 000 : toutes les roues repassent à 0.

En hexadécimal, 9 + 8 = ?

17 ≥ 16 : on écrit 17 − 16 = 1 et on retient 1. Résultat (11)₁₆.

En binaire, 1000 − 1 = ?

8 − 1 = 7 = (111)₂. Les trois zéros traversés deviennent des 1.

5Taille fixe : ce que fait vraiment la machine

Sur papier, quand une addition dépasse, on ajoute un chiffre à gauche. Une machine ne peut pas : un registre de 8 bits a 8 cases, pas une de plus. Le bit qui devrait sortir à gauche est perdu pour le résultat. On l'appelle la retenue sortante (carry out).

11111111retenues
11111111255
+000000011
1000000000 : la retenue est sortie

Sur 8 bits, 255 + 1 donne 0. Le compteur kilométrique fait pareil : après 999 999 km, il affiche 000 000.

Entiers non signés (unsigned) sur n bits

Ils vont de 0 à 2n − 1. Quand une addition produit une retenue sortante, le résultat affiché est faux : le vrai résultat ne tient pas sur n bits. Le processeur range ce bit dans un indicateur (flag) appelé C, pour carry.

Autrement dit, sur n bits, la machine calcule modulo 2n : elle ne garde que le reste de la division par 2n. 255 + 1 = 256, et 256 modulo 256 = 0. Retiens bien cette idée : c'est elle qui va permettre de représenter les nombres négatifs.

En Python, les entiers n'ont pas de taille fixe : 255 + 1 donne 256. Mais en C, en Java, ou avec NumPy, les entiers ont une taille fixe et ce phénomène arrive vraiment (section 7.6).

6Les nombres négatifs

6.1 Le problème

En mémoire, il n'y a pas de signe « − » : il n'y a que des bits. Il faut donc une convention, une règle fixée à l'avance qui dit quelles suites de bits représentent des nombres négatifs. Tes notes en contiennent trois, présentées dans un ordre logique : chacune corrige un défaut de la précédente.

Le principe le plus important de cette page

Une suite de bits ne veut rien dire toute seule. 1001 0110 vaut 150, −22, −105 ou −106 selon la convention avec laquelle on la lit. Ce n'est pas la mémoire qui décide, c'est le programme, à travers le type de la variable.

C'est comme la date 03/04 : le 3 avril en France, le 4 mars aux États-Unis. Les chiffres sont les mêmes, la convention de lecture change tout.

6.2 Signe et valeur absolue

C'est la première idée de tes notes (page 5) : on réserve le bit de gauche, appelé bit de poids fort (most significant bit), au signe. 0 pour +, 1 pour −. Les 7 autres bits donnent la valeur absolue (sign-magnitude).

nombresignevaleur absolue sur 7 bitssur 8 bits
+50000 01010000 0101
−51000 01011000 0101
−11000 00011000 0001

Sur 8 bits, on va de −127 à +127. C'est simple à lire, mais il y a deux défauts graves.

1retenues
00000001+1
+10000001−1 en signe et valeur absolue
10000010−2 : faux

Il faudrait un circuit spécial qui compare les signes, puis additionne ou soustrait les valeurs absolues. C'est faisable, mais lourd.

6.3 Complément à un

Deuxième idée, avec ta définition : « je prends la valeur positive et je l'inverse ». Pour obtenir −x, on inverse chaque bit de x : les 0 deviennent 1 et les 1 deviennent 0 (ones' complement).

70000 0111 −71111 1000 Chaque bit est changé sur place.

Inverser n'est pas retourner

Page 6 de tes notes, tu as écrit −7 = 1110 0000 en complément à un. C'est 0000 0111 lu dans un miroir : tu as retourné l'ordre des bits. Inverser, c'est changer chaque bit sans le déplacer. Tu l'as d'ailleurs corrigé un peu plus bas, avec −7 = 1111 1000.

Le bit de gauche indique toujours le signe, et on va encore de −127 à +127. Il reste deux zéros : 0000 0000 et 1111 1111 (le « −0 = 1111 1111 » de tes notes).

L'addition marche presque, à une correction près, la retenue circulaire (end-around carry) : si une retenue sort à gauche, on ne la jette pas, on la rajoute au résultat. Exemple, 5 + (−3) :

111111retenues
000001015
+11111100−3 en complément à un
100000001une retenue sort : on la garde
1retenues
00000001résultat sans la retenue
+00000001+ la retenue circulaire
000000102 : juste

Pourquoi ce + 1 ? Inverser 8 bits, c'est calculer 255 − x : on soustrait x de 1111 1111, colonne par colonne, sans jamais d'emprunt. Donc en complément à un, −3 est codé par 255 − 3. Quand la somme dépasse 255, la retenue qui sort fait perdre 256, soit 1 de trop par rapport aux 255 utilisés. On rend ce 1 en l'ajoutant.

C'est l'exemple sur 16 bits en bas de la page 8 de tes notes : 3 292 + (−1 406) = 1 886.

111111retenues
00001100110111003 292
+1111101010000001−1 406 en complément à un
10000011101011101une retenue sort
1retenues
0000011101011101résultat sans la retenue
+0000000000000001+ la retenue circulaire, une fois
00000111010111101 886

Une seule retenue circulaire

La retenue circulaire s'ajoute une seule fois. Dans tes notes, tu as ajouté 1 une deuxième fois, dans le cadre du bas, et obtenu 0000 0111 0101 1111. Ce deuxième + 1 est en trop : le résultat est 0000 0111 0101 1110 = 1 886.

Restent deux défauts : deux zéros, et une addition qui demande parfois un deuxième passage dans l'additionneur.

6.4 Complément à deux

C'est la convention de tous les processeurs actuels, le tien compris. Pour obtenir −x, on inverse tous les bits, puis on ajoute 1 (two's complement). C'est la ligne de tes notes : « complément à 1, et je rajoute 1 à la fin ».

70000 0111 inversé1111 1000 + 11111 1001 = −7
xx en binaireinversé+ 1 = −x
10000 00011111 11101111 1111
70000 01111111 10001111 1001
320010 00001101 11111110 0000
580011 10101100 01011100 0110
1000110 01001001 10111001 1100
1270111 11111000 00001000 0001

Les lignes 32 et 58 sont dans tes notes, et elles sont justes.

Pourquoi « inverser puis ajouter 1 » donne l'opposé

Reviens à la section 5 : sur 8 bits, la machine calcule modulo 256. Ajouter 256 ne change rien, puisque ce 256 est une retenue qui tombe à gauche. Pour la machine, −7 et 256 − 7 = 249 sont donc le même nombre. On représente −7 par le motif de 249 : 1111 1001.

11111111retenues
000001117
+11111001249, c'est-à-dire −7
1000000000 (la retenue tombe)

Ajouté à 7, ce motif donne 0 : il se comporte exactement comme −7. Et « inverser puis + 1 » calcule justement 256 − x : inverser donne 255 − x (section 6.3), et ajouter 1 donne 256 − x.

L'image de l'horloge

Sur une horloge de 12 heures, reculer de 3 heures ou avancer de 9 heures mène à la même heure : pour l'horloge, −3 et +9 sont le même déplacement. Sur 8 bits, l'horloge a 256 positions, et −7 se confond avec +249. Le laboratoire de la section 7.4 dessine cette horloge pour 4 bits.

Lire un nombre négatif

Astuce de calcul

Pour obtenir −x plus vite : en partant de la droite, recopie les bits jusqu'au premier 1 inclus, puis inverse tous les autres. Pour 58 = 0011 1010, on recopie « 10 », on inverse « 001110 » en « 110001 », et on obtient 1100 0110.

6.5 Les trois conventions côte à côte, sur 4 bits

Les lignes grisées commencent par un 1 : ce sont celles que chaque convention lit comme négatives, ou comme −0 (sauf en non signé, bien sûr).

bitsnon signésigne et valeur absoluecomplément à uncomplément à deux
00000000
00011111
00102222
00113333
01004444
01015555
01106666
01117777
10008−0−7−8
10019−1−6−7
101010−2−5−6
101111−3−4−5
110012−4−3−4
110113−5−2−3
111014−6−1−2
111115−7−0−1

Regarde la dernière colonne : de 0000 à 0111, on compte de 0 à 7 ; puis à 1000, on saute à −8 et on remonte jusqu'à −1. Les négatifs sont les grands nombres non signés, décalés de 16 : 1001 vaut 9 en non signé et 9 − 16 = −7 en complément à deux.

6.6 Laboratoire : une même suite de bits, plusieurs lectures

C'est un savoir-faire demandé par ta prof : interpréter une même suite de bits selon différentes conventions. Clique sur les bits et compare les lectures.

6.7 Passer de 8 à 16 bits : l'extension de signe

Pour écrire un nombre signé sur plus de bits, on recopie le bit de signe à gauche (sign extension) :

+70000 0111 → 0000 0000 0000 0111 −71111 1001 → 1111 1111 1111 1001

Compléter un négatif avec des zéros changerait sa valeur : 0000 0000 1111 1001 vaut 249. Avec des 1, c'est juste : sur 16 bits, −7 se code par 65 536 − 7 = 65 529, qui s'écrit bien 1111 1111 1111 1001.

Vérifie-toi (section 6)

En complément à deux sur 8 bits, quel est l'opposé de 0001 0100 (20) ?

Inversé : 1110 1011, puis + 1 : 1110 1100. Vérifie : −128 + 64 + 32 + 8 + 4 = −20.

Que vaut 1111 1111 en complément à deux ?

−128 + 127 = −1. C'est dans tes notes : « −1 = 1111 1111 ».

Sur 8 bits en complément à deux, la plage est…

256 motifs : 128 négatifs (−128 à −1), le zéro, et 127 positifs.

Combien de zéros différents y a-t-il en complément à un ?

0000 0000 et 1111 1111. C'est un des défauts que le complément à deux corrige.

Pour passer −3 (1111 1101) de 8 à 16 bits, on écrit…

On recopie le bit de signe : extension de signe.

7Additionner et soustraire des nombres signés

7.1 La bonne nouvelle

En complément à deux, on additionne exactement comme en non signé, avec le même circuit, et on ignore la retenue qui sort à gauche. Sur 8 bits, elle vaut 256, et l'enlever ne change rien au calcul modulo 256 (section 6.4).

Et la soustraction n'a pas besoin de circuit à elle : A − B se calcule comme A + (−B). C'est la raison pour laquelle tous les processeurs utilisent le complément à deux : un seul additionneur fait les additions et les soustractions, sur les nombres signés comme non signés. Ta note sur l'UAL, « addition seulement », vient de là (on la précise en section 9).

7.2 La méthode, en six étapes

Calculer A + B ou A − B en complément à deux sur n bits

  1. Fixe la taille n (8 bits, sauf indication contraire), et vérifie que A et B tiennent dans la plage, de −128 à 127 sur 8 bits.
  2. Code chaque nombre. Un positif s'écrit en binaire, complété à gauche par des 0. Un négatif : on code sa valeur absolue, on inverse, on ajoute 1.
  3. Si c'est une soustraction, remplace − B par + (−B), en prenant le complément à deux de B.
  4. Additionne colonne par colonne, et ignore la retenue qui sort à gauche.
  5. Contrôle le débordement : si A et (±B) ont le même signe et que le résultat a le signe opposé, il est faux (section 7.4). Cas particulier : −(−128) n'existe pas sur 8 bits, donc soustraire −128 déborde dès que A est positif ou nul.
  6. Lis le résultat (si le bit de gauche vaut 1, il est négatif : section 6.4) et vérifie en décimal.

Règle d'or de l'étape 2 : on ne prend l'opposé que des nombres précédés d'un signe −. Dans 32 + (−58), seul 58 est négatif.

7.3 Cinq exemples détaillés

Exemple 1 : 32 + (−58), l'exemple de tes notes

320010 0000 −581100 0110 58 = 0011 1010, inversé 1100 0101, + 1.
0010000032
+11000110−58
11100110−26

Le bit de gauche vaut 1 : le résultat est négatif. On refait « inverser + 1 » : 0001 1001 + 1 = 0001 1010 = 26. Le résultat est −26, et 32 − 58 = −26.

Ce qui s'est passé dans tes notes

Tu as posé 1110 0000 + 1100 0110, c'est-à-dire (−32) + (−58). Ton addition est juste (elle donne −90, voir l'exemple 3), mais ce n'était pas le calcul demandé : dans 32 + (−58), le 32 est positif. Plus bas, pour 58 + (−32), tu as de nouveau pris −58 au lieu de 58.

Exemple 2 : 58 + (−32), avec une retenue qui sort

111retenues
0011101058
+11100000−32
10001101026 (retenue ignorée)

Une retenue sort à gauche. En complément à deux, ce n'est pas une erreur : on l'ignore, et le résultat 0001 1010 = 26 est juste.

Exemple 3 : (−32) + (−58), le calcul que tu as fait

11retenues
11100000−32
+11000110−58
110100110−90 (retenue ignorée)

Lecture : −128 + 32 + 4 + 2 = −90. Deux négatifs donnent un négatif : tout va bien.

Exemple 4 : une soustraction, 45 − 100

45 − 100 = 45 + (−100) 450010 1101 −1001001 1100 100 = 0110 0100, inversé 1001 1011, + 1.
1111retenues
0010110145
+10011100−100
11001001−55

Lecture : −128 + 64 + 8 + 1 = −55. Et 45 − 100 = −55.

Exemple 5 : soustraire un négatif, −20 − (−50)

−20 − (−50) = −20 + 50 Soustraire −50, c'est ajouter son opposé, +50. −201110 1100 500011 0010
111retenues
11101100−20
+0011001050
10001111030 (retenue ignorée)

Retenue ignorée, résultat 0001 1110 = 30.

7.4 Retenue ou débordement ?

Ta prof demande de distinguer ces deux phénomènes, que presque tout le monde confond au début.

Retenue (carry), indicateur CDébordement (overflow), indicateur V
Ce que c'estle bit qui sort à gauche de l'additionle vrai résultat signé sort de la plage (−128 à 127 sur 8 bits)
Qui est concernéles entiers non signésles entiers signés en complément à deux
Comment le repérerune retenue sort de la colonne de gauchedeux nombres de même signe donnent un résultat de signe opposé
Ce qu'il signifieen addition, le résultat non signé est fauxle résultat signé est faux

Ce sens de C vaut pour l'addition. Après une soustraction faite comme A + (−B), la retenue change de sens : sur les processeurs ARM, C = 1 veut dire « pas d'emprunt », et les processeurs x86 l'inversent. Pour une soustraction, retiens surtout V.

La règle du débordement

Il y a débordement quand on additionne deux nombres de même signe et qu'on obtient un résultat de l'autre signe : positif + positif = négatif, ou négatif + négatif = positif.

Si les deux nombres sont de signes opposés, le débordement est impossible : le résultat est entre les deux, donc il tient forcément.

Le circuit utilise une règle équivalente : il y a débordement quand la retenue qui entre dans la colonne de gauche est différente de celle qui en sort. « Différent », c'est la porte XOR de la page sur les portes logiques.

Les quatre cas possibles, sur 8 bits. Chaque addition est lue deux fois : en non signé et en signé.

C = 0, V = 0 : 5 + 3

111retenues
000001015
+000000113
000010008 : juste dans les deux lectures

C = 1, V = 0 : (−1) + (−1)

11111111retenues
11111111255 non signé, −1 signé
+11111111255 non signé, −1 signé
111111110non signé 254 : faux ; signé −2 : juste

En signé, le résultat −2 est juste. En non signé, ces bits valent 255 + 255 = 510, qui ne tient pas sur 8 bits : la retenue le signale.

C = 0, V = 1 : 100 + 50

11retenues
01100100100
+0011001050
10010110non signé 150 : juste ; signé −106 : faux

Positif + positif donne un nombre qui commence par 1 : négatif. Le vrai résultat, 150, dépasse 127. En non signé, en revanche, 100 + 50 = 150 est juste.

C = 1, V = 1 : (−100) + (−50)

1111retenues
10011100156 non signé, −100 signé
+11001110206 non signé, −50 signé
101101010non signé 106 : faux ; signé 106 : faux

Négatif + négatif donne un positif : −150 est en dessous de −128. Faux dans les deux lectures.

Laboratoire : le cercle des 4 bits

Voici l'horloge de la section 6.4, pour 4 bits. À l'extérieur, la lecture non signée (0 à 15) ; à l'intérieur, le complément à deux (−8 à 7). Clique sur une case pour choisir A, et règle B avec ses 4 bits. L'arc extérieur montre l'addition non signée : s'il franchit la frontière du haut (15 → 0), C = 1. L'arc intérieur montre l'addition signée : s'il franchit la frontière du bas (7 → −8), V = 1. Les deux arcs arrivent sur la même case : ce sont les mêmes bits, lus de deux façons.

7.5 Laboratoire : une mini-UAL sur 8 bits

Tape deux nombres entre −128 et 255, ou deux suites de 8 bits, puis choisis + ou −. La page applique la méthode de la section 7.2 et affiche les indicateurs C et V, avec les deux lectures du résultat.

7.6 Pourquoi ça compte en sécurité

Débordement d'entier (integer overflow)

En Python, un entier grandit autant qu'il faut : pas de débordement. Mais dans les langages où les entiers ont une taille fixe (C, C++, Java, Rust compilé en mode release), un débordement peut passer inaperçu. C'est une famille de failles bien connue, répertoriée sous le nom CWE-190 (Integer Overflow or Wraparound) : un calcul de taille qui déborde donne un petit nombre, et un test de sécurité passe alors qu'il devrait échouer. En C ou en C++, le programme peut alors écrire en dehors de la mémoire prévue ; Java et Rust vérifient les bornes des tableaux et s'arrêtent sur une erreur. En C, le débordement d'un entier signé n'est même pas garanti de « faire le tour » : c'est un comportement indéfini.

Et ça te concerne aussi en IA : les modèles quantifiés stockent leurs poids en int8, c'est-à-dire en complément à deux sur 8 bits, de −128 à 127. Tu peux le voir sur ta machine :

>>> import numpy as np
>>> np.array([127], dtype=np.int8) + 1
array([-128], dtype=int8)

127 + 1 = −128 : on vient de franchir la frontière du bas du cercle.

Vérifie-toi (section 7)

Sur 8 bits en complément à deux, que donne 100 + 100 ?

0110 0100 + 0110 0100 = 1100 1000 = −56 : positif + positif = négatif, V = 1. Et C = 0.

Peut-il y avoir débordement en calculant 120 + (−120) ?

Signes opposés : jamais de débordement. Il y aura une retenue sortante, mais elle est ignorée.

Pour calculer 30 − 12 en complément à deux, la machine calcule…

A − B = A + (−B), avec −12 = 1111 0100.

Une retenue sort à gauche pendant une addition en complément à deux. Le résultat signé est…

En signé, la retenue sortante ne veut rien dire. Seul le débordement V compte.
À poser sur ta feuille, puis à vérifier
  1. Calcule 75 − 90 sur 8 bits.
    Réponse
    75 = 0100 1011 ; −90 = 1010 0110 (90 = 0101 1010, inversé 1010 0101, + 1). Somme : 1111 0001, pas de retenue, pas de débordement. Lecture : −128 + 64 + 32 + 16 + 1 = −15.
  2. Calcule −100 − 30 sur 8 bits. Que remarques-tu ?
    Réponse
    −100 = 1001 1100 ; −30 = 1110 0010. Somme : 1 0111 1110, on ignore la retenue : 0111 1110 = 126. Négatif + négatif = positif : débordement, V = 1. Le vrai résultat, −130, est en dessous de −128.
  3. Calcule 127 + 1 sur 8 bits, et compare avec la sortie de NumPy en 7.6.
    Réponse
    0111 1111 + 0000 0001 = 1000 0000 = −128. Débordement : c'est exactement ce qu'affiche NumPy.

8Aperçu : caractères et nombres à virgule

Le programme de la séance du 28 septembre mentionne un « aperçu » de ces deux codages. Voici l'essentiel.

8.1 Les caractères

Un texte est une suite de nombres : chaque caractère a un numéro, fixé par une table. La plus ancienne, ASCII, numérote 128 caractères sur 7 bits.

caractèredécimalhexabinaire
espace32200010 0000
'0'48300011 0000
'9'57390011 1001
'A'65410100 0001
'Z'905A0101 1010
'a'97610110 0001

Aujourd'hui, on utilise Unicode, qui numérote plus de 150 000 caractères, et UTF-8, qui les écrit sur 1 à 4 octets. Les caractères ASCII gardent leur octet unique ; « é » prend 2 octets, C3 A9. Si un texte UTF-8 est relu octet par octet comme un ancien codage (Latin-1), chaque octet devient un caractère, et « é » s'affiche « é ». Tu as sûrement déjà vu ce bug sur des sites mal configurés.

8.2 Les nombres à virgule

Virgule fixe. On prolonge les poids après la virgule : 2⁻¹ = 0,5, 2⁻² = 0,25, 2⁻³ = 0,125… Ainsi (101,1)₂ = 4 + 1 + 0,5 = 5,5.

Virgule flottante (floating point). On écrit le nombre comme en notation scientifique, 6,02 × 10²³, mais en base 2 : un bit de signe, un exposant, et une mantisse (significand) qui contient les chiffres significatifs. La norme IEEE 754 fixe deux formats courants :

formattotalsigneexposantmantisse
simple précision (float)32 bits1823
double précision (double, le float de Python)64 bits11152

Conséquence importante : 0,1 n'a pas d'écriture binaire finie. Il s'écrit (0,000110011001100…)₂, avec une suite qui se répète sans fin, comme 1/3 = 0,333… en décimal. La machine doit arrondir, et :

>>> 0.1 + 0.2
0.30000000000000004
>>> 0.1 + 0.2 == 0.3
False

Évite donc de comparer deux flottants avec ==. En Python, utilise plutôt math.isclose.

9Relecture de tes notes

Ce qui est juste

Ce qu'il faut corriger

  1. −7 en complément à un (page 6). 1110 0000 est le miroir de 0000 0111, pas son inversion. Complément à un : 1111 1000 ; complément à deux : 1111 1001 (section 6.3).
  2. La liste de −7 à −11 (page 6). Les valeurs 1111 1000, 1111 0111, 1111 0110, 1111 0101, 1111 0100 sont en complément à un. Écris-le à côté. En complément à deux, chaque valeur a 1 de plus : −7 = 1111 1001, −8 = 1111 1000, −9 = 1111 0111, −10 = 1111 0110, −11 = 1111 0101. Complète les lignes −12 à −15, restées vides, dans les deux conventions.
  3. 32 + (−58) et 58 + (−32) (page 7). Tu as utilisé −32 au lieu de 32, puis −58 au lieu de 58. Tes additions calculent en fait (−32) + (−58) = −90 (section 7.3).
  4. L'addition en octal (page 8). 125672 + 126334 = 254226, pas 154226 : dans la colonne de gauche, 1 + 1 = 2. Vérification : 43 962 + 44 252 = 88 214 = (254226)₈.
  5. Un titre inversé (page 7). 1010 1011 1011 1010 → (125672)₈ est la conversion de ABBA, pas de ACDC.
  6. Le complément à un sur 16 bits (page 8). La retenue circulaire s'ajoute une seule fois : le résultat est 0000 0111 0101 1110 = 1 886 (section 6.3).
  7. « 1000 0000 = −0 », « 1000 0001 = −1 » (page 5) : c'est juste, mais seulement en signe et valeur absolue. « −0 = 1111 1111 » est du complément à un, et « −1 = 1111 1111 » du complément à deux. Écris toujours la convention à côté d'un nombre signé : la même suite de bits change de valeur d'une convention à l'autre.
  8. « UAL : addition seulement » (page 5). L'UAL (unité arithmétique et logique, ALU) fait aussi des opérations logiques (ET, OU, XOR…) : c'est le L de UAL. Ce qui est vrai, c'est qu'il n'y a pas de circuit de soustraction séparé (section 7.1).
  9. « RAM : donne les instructions au processeur » (page 5). La RAM ne fait rien d'elle-même : elle stocke les instructions et les données, et c'est le processeur qui va les chercher. Tu verras ce cycle le 16 novembre.
  10. « 32 cases = 2³¹ » (page 5). Sur 32 bits, il y a 2³² motifs. L'idée de tes notes, c'est qu'un bit sert au signe : en complément à deux, on va de −2³¹ à 2³¹ − 1, soit environ ± 2,1 milliards.

10Bilan

Avant l'évaluation du 2 novembre, tu dois pouvoir, sans tes notes…

  • dire combien de valeurs on écrit avec n bits, et combien de bits il faut pour N valeurs ;
  • convertir entre les bases 2, 8, 10 et 16 dans tous les sens, et expliquer pourquoi on lit les restes de bas en haut ;
  • poser une addition et une soustraction en base 2, 8 et 16, avec retenues et emprunts ;
  • coder un entier en signe et valeur absolue, en complément à un et en complément à deux, et dire les défauts des deux premiers ;
  • expliquer pourquoi « inverser puis + 1 » donne l'opposé (l'horloge modulo 256) ;
  • calculer A + B et A − B en complément à deux, et dire s'il y a retenue, débordement, les deux ou aucun ;
  • lire une même suite de bits en non signé et en complément à deux.

La suite : la page Les portes logiques, depuis le tout début, pour la séance du 5 octobre. Tu y verras comment deux portes suffisent pour additionner deux bits.

↑