Le binaire et les bits
Suivre un message jusqu'à ses octets pour comprendre les bits, les nombres et l'hexadécimal.
Une question concrète
Imaginez que vous rangez le texte Salut sous le nom message (en JavaScript cela s'écrirait const message = "Salut" — le langage sera disséqué au chapitre 4, lisez-le ici comme une intention). Comment ces lettres peuvent-elles être enregistrées dans un fichier ou envoyées à un autre ordinateur ? Le disque et le réseau transportent des signaux, pas des lettres. Il faut convenir d'une façon de représenter l'information avec ces signaux. Nous allons suivre une petite partie du trajet : la lettre S devient le nombre 83, puis l'octet 01010011. La convention qui associe la lettre au nombre sera détaillée dans la quatrième leçon.
À quoi cela sert-il en développement ? À comprendre pourquoi une taille de fichier est comptée en octets, pourquoi un texte accentué peut occuper plus de place qu'attendu, et pourquoi une adresse IP contient quatre nombres entre 0 et 255.
🔗 Vous avez déjà rencontré le chemin du programme au processus. Ici, nous regardons comment ses données sont représentées.
Un bit : choisir entre deux états
Imaginez huit interrupteurs, chacun éteint ou allumé. Chaque interrupteur porte un bit : on note ses deux états 0 et 1. Dans un circuit réel, ce sont des plages de tension interprétées comme 0 ou 1 ; 0 ne signifie pas nécessairement « aucun courant ».
Interrupteurs : éteint allumé éteint allumé éteint éteint allumé allumé
Bits : 0 1 0 1 0 0 1 1
Un bit donne 2 possibilités. Ajouter un interrupteur double le nombre de combinaisons : 2 bits donnent 4 possibilités (00, 01, 10, 11), 3 bits en donnent 8. Huit bits, soit un octet, donnent 2⁸ = 256 combinaisons, numérotées de 0 à 255. Cela explique la plage de chaque nombre dans une adresse IPv4 comme 192.168.1.10 : chacun représente un octet. Une adresse complète contient quatre octets, donc 32 bits.
Un octet peut représenter un petit nombre, une partie de texte ou une partie d'image. Les bits ne portent pas leur sens tout seuls : un programme doit connaître la convention utilisée pour les interpréter.
Lire un octet comme un nombre
Nos nombres habituels utilisent dix chiffres et des positions de valeur 1, 10, 100…. Le binaire utilise deux chiffres et des positions de valeur 1, 2, 4, 8…. Pour notre octet, lisez de gauche à droite :
Valeur de position : 128 64 32 16 8 4 2 1
Bits : 0 1 0 1 0 0 1 1
Valeurs retenues : 64 16 2 1
Donc 01010011 vaut 64 + 16 + 2 + 1 = 83. Si l'on interprète cet octet comme du texte ASCII ou UTF-8, 83 désigne S. Si on l'interprète comme un nombre, c'est simplement 83 : mêmes bits, sens différent.
Pour aller dans l'autre sens, prenons 13. La plus grande valeur de position qui tient dans 13 est 8 ; il reste 5. On prend 4 ; il reste 1. On prend 1 : 8 + 4 + 1, donc 1101. Sur un octet, on ajoute quatre zéros devant : 00001101. Ces zéros ne changent pas la valeur, mais montrent les huit positions disponibles.
Vous n'aurez généralement pas à convertir des octets à la main. Le calcul sert ici à comprendre d'où vient la limite 255, et à lire les exemples de réseau et de mémoire qui suivent.
L'hexadécimal : une écriture plus courte
Pour montrer des octets, huit chiffres binaires prennent beaucoup de place. L'idée est simple : 16 = 2⁴, donc un chiffre hexadécimal représente exactement quatre bits, et deux chiffres représentent un octet entier. C'est pour cela qu'on coupe l'octet en deux groupes de quatre bits :
0101 0011 → 5 3 → 0x53
1111 1111 → F F → 0xFF → 255 en décimal
Un groupe de quatre bits offre 16 valeurs (de 0000 à 1111, soit 0 à 15). L'hexadécimal les écrit avec seize symboles : 0 à 9 pour 0 à 9, puis A à F pour 10 à 15 :
| Décimal | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 4 bits | 0000 | 0001 | 0010 | 0011 | 0100 | 0101 | 0110 | 0111 | 1000 | 1001 | 1010 | 1011 | 1100 | 1101 | 1110 | 1111 |
| Hexa | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F |
Déroulons les deux exemples. 01010011 se coupe en 0101 (= 4 + 1 = 5) et 0011 (= 2 + 1 = 3), donc 0x53. Dans l'autre sens, 0xFF signifie F (= 15 = 1111) deux fois, donc 11111111, soit 128 + 64 + 32 + 16 + 8 + 4 + 2 + 1 = 255. Ainsi 0x53, 83 et 01010011 désignent le même nombre dans trois écritures.
Vous croiserez plusieurs notations pour la même valeur, selon l'outil : 0x53 en JavaScript et en shell, #53 ou #FF0000 en CSS (#FF0000 : rouge à FF = 255, vert et bleu à 00 = 0), 53 seul dans Buffer.from("S", "utf8") qui affiche <Buffer 53> (le 0x est sous-entendu), ou 53 dans od -An -tx1. Retenez : quand vous voyez deux chiffres hexadécimaux collés, lisez « un octet ».
Vous rencontrerez l'hexadécimal dans les couleurs CSS, les adresses IPv6 et les outils qui affichent les octets d'un fichier. Les couleurs web s'écrivent ainsi — aucun besoin de connaître CSS pour la suite. La quatrième leçon s'en servira pour lire C3 A9 (é en UTF-8) : deux chiffres = un octet, deux octets = une lettre accentuée.
Vérifier avec Node.js
Ouvrez un terminal et lancez node : vous obtenez un prompteur interactif (un REPL : il lit chaque ligne, l'évalue et affiche le résultat). Le JavaScript sera disséqué au chapitre 4 ; ici, recopiez ligne par ligne et comparez les affichages — chaque commentaire // dit ce que fait la ligne :
(83).toString(2) // "1010011" : 83 affiché en binaire (sans les zéros de tête)
(83).toString(2).padStart(8, "0") // "01010011" : complété à 8 chiffres avec des zéros à gauche
(83).toString(16) // "53" : 83 affiché en hexadécimal
parseInt("01010011", 2) // 83 : lit la chaîne comme du binaire
parseInt("53", 16) // 83 : lit la chaîne comme de l'hexadécimal
Buffer.from("S", "utf8") // <Buffer 53> : les octets du texte "S" (Buffer = boîte à outils des octets en Node)
toString(2) change l'écriture affichée, pas le nombre. Buffer montre les octets obtenus en encodant le texte ; nous expliquerons cet encodage dans la quatrième leçon.
Attention : parseInt peut accepter un début de chaîne valide et ignorer la suite. parseInt("10xyz", 2) vaut 2, alors que parseInt("77", 2) vaut NaN : aucun chiffre initial n'est valide en base 2. Ce n'est donc pas un validateur de chaîne binaire.
À essayer
- Dessinez quatre interrupteurs et écrivez toutes les combinaisons, de
0000à1111. Pourquoi y en a-t-il 16 ? - Avec les valeurs de position ci-dessus, trouvez le nombre représenté par
00001010, puis celui représenté par11111111. - Représentez 13 sur huit bits. Vérifiez avec
(13).toString(2).padStart(8, "0"). - Convertissez
0xC3en binaire puis en décimal (aide :C= 12 =1100). Vérifiez avecparseInt("C3", 16). #FF0000: quelle intensité pour le rouge, le vert, le bleu ? Convertissez255en hexa avec(255).toString(16).- Une adresse IPv4 contient quatre octets. Pourquoi chaque partie peut-elle valoir 255 au maximum ?
Ce qu'il faut retenir
Un bit distingue deux états ; huit bits forment un octet et offrent 256 combinaisons. Le binaire, le décimal et l'hexadécimal sont des écritures de valeurs. Pour comprendre ce que signifient des octets, il faut connaître la convention de lecture : texte, nombre, couleur, instruction…
Correction disponibleCherchez d’abord par vous-même.Voir la correction
Correction
- Chaque interrupteur double les possibilités :
2 × 2 × 2 × 2 = 16. Les combinaisons vont de0000à1111. 00001010 = 8 + 2 = 10.11111111 = 128 + 64 + 32 + 16 + 8 + 4 + 2 + 1 = 255.13 = 8 + 4 + 1, donc00001101. La commande affiche bien"00001101".0xC3:C= 12 =1100,3=0011, donc11000011=128 + 64 + 2 + 1 = 195. C'est le premier octet deéen UTF-8 (voir leçon 03-04).#FF0000: rougeFF= 255 (maximal), vert00= 0, bleu00= 0.(255).toString(16)affiche"ff"(majuscules/minuscules équivalentes en hexa).- Chaque partie représente huit bits, soit 256 valeurs numérotées de 0 à 255. Les quatre parties représentent ensemble 32 bits.