Résumé
Un entier du programme n’est pas un entier des mathématiques : c’est une suite finie de bits, et avec bits on peut représenter exactement valeurs distinctes. Cette leçon étudie cette représentation et ses conséquences. Elle enseigne à écrire un entier en base 2 et en base 16 et à le vérifier avec les spécifications de format {:b} et {:x} ; elle présente les types entiers de C++, leur taille, qui se mesure avec sizeof, et leur plage, qui se consulte avec std::numeric_limits ; elle explique comment on représente les nombres strictement négatifs au moyen du complément à deux et pourquoi la plage n’est pas symétrique ; elle distingue les entiers signés des entiers non signés, et elle étudie ce qui se passe quand une opération sort de la plage : pour les non signés, la norme définit le résultat, qui est l’arithmétique modulo ; pour les signés, elle ne le définit pas, et le programme échappe à toute garantie. Elle se termine par un critère pour choisir le type de chaque donnée.
Objectifs d’apprentissage
- Convertir des entiers entre les bases 10, 2 et 16, et vérifier la conversion avec les spécifications de format
{:b}et{:x}. - Calculer la plage d’un entier de bits signé et non signé, et la vérifier avec
std::numeric_limitsetsizeof. - Expliquer le complément à deux et écrire la représentation sur huit bits d’un petit nombre strictement négatif.
- Distinguer le dépassement signé, qui est un comportement indéfini, du dépassement non signé, qui est l’arithmétique modulo , et prédire le résultat du second.
- Choisir le type entier adapté à une donnée selon sa plage prévisible, en utilisant les types à largeur fixe de
<cstdint>quand la largeur importe.
Systèmes positionnels
La leçon Variables et mémoire a présenté la variable comme une case dotée d’un nom, d’un type et d’une valeur. Or la mémoire ne garde pas de chiffres décimaux : elle garde des bits, dont chacun vaut 0 ou 1, regroupés en octets de huit bits (la norme en exige au moins huit ; tous les systèmes actuels en utilisent exactement huit). Pour savoir quelles valeurs peut prendre une variable entière, il faut donc savoir comment on écrit un nombre avec deux chiffres seulement, et c’est ce que fait un système positionnel de base 2.
Dans un système positionnel de base , chaque chiffre vaut selon la position qu’il occupe : le dernier multiplie , celui d’avant , celui d’avant encore , et ainsi de suite. En base 10, signifie . En base 2 les chiffres sont 0 et 1, et les poids sont les puissances de 2 : Il en découle la règle pour passer du binaire au décimal : on additionne les poids des positions où se trouve un 1.
La figure lit le motif de huit bits 00101101. Au-dessus de chaque case se trouve son poids ; les uns s’allument un à un, de gauche à droite, et la valeur cumule leurs poids.
Démonstration
Huit cases, toutes éteintes : la valeur est 0. Les poids, de droite à gauche, sont .
Le premier un se trouve à la position de poids 32 : la valeur cumulée est 32.
Le suivant, à celle de poids 8 : .
Le suivant, à celle de poids 4 : .
Le dernier, à celle de poids 1 : . Le motif
00101101représente le nombre 45.Regroupés quatre par quatre, les bits donnent les chiffres hexadécimaux :
0010vaut 2 et1101vaut 13, qui s’écritden base 16. Par conséquent, 45 s’écrit2den base 16.
Le chemin inverse, du décimal au binaire, se parcourt par divisions successives : on divise le nombre par 2, on note le reste (0 ou 1) et on recommence avec le quotient jusqu’à arriver à 0 ; les restes, lus du dernier au premier, sont les chiffres binaires. Pour 45 : , , , , , ; lus à rebours, les restes donnent 101101, qui, avec deux zéros à gauche, est le motif de la figure. La raison en est que chaque reste est le chiffre des unités du nombre qu’on divise, et que chaque quotient est ce nombre décalé d’une position vers la droite. La même méthode vaut pour n’importe quelle base : pour la base 16 on divise par 16, et les restes de 10 à 15 s’écrivent avec les lettres a à f.
La base 16, ou hexadécimal, existe par commodité : comme , chaque chiffre hexadécimal correspond exactement à quatre bits, et un octet s’écrit avec deux chiffres. Un nombre de 32 bits occupe trente-deux chiffres en binaire, qui se lisent mal, et seulement huit en hexadécimal, qui se convertissent en binaire sans calcul, chiffre par chiffre. C’est pourquoi les programmeurs lisent la mémoire en hexadécimal. Le fondement mathématique des systèmes positionnels s’étudie dans le cours Algèbre et géométrie I, dans la leçon « Systèmes de numération de position ».
C++ admet des entiers écrits directement dans ces bases : un littéral qui commence par 0b est en binaire, et un qui commence par 0x, en hexadécimal. À l’intérieur de tout littéral, on peut placer une apostrophe comme séparateur de chiffres, que le compilateur ignore et que le lecteur apprécie : 0b0010'1101, 1'000'000. En sortie, la spécification de format {:b} écrit un entier en binaire, {:x} en hexadécimal avec des minuscules et {:X} avec des majuscules ; le signe # ajoute le préfixe devant, et une largeur précédée d’un zéro complète avec des zéros à gauche.
#include <print>int main(){ const int decimal{45}; const int binary{0b0010'1101}; const int hexadecimal{0x2d}; std::println("{} {} {}", decimal, binary, hexadecimal); std::println("binaire : {:b}", decimal); std::println("huit bits : {:08b}", decimal); std::println("hexadécimal : {:x} {:X} {:#x}", decimal, decimal, decimal);}45 45 45binaire : 101101huit bits : 00101101hexadécimal : 2d 2D 0x2dLes trois variables gardent la même valeur, écrite dans trois bases, et la première ligne de la sortie le montre : la base est une propriété de l’écriture, non du nombre. La mémoire garde dans les trois cas le même motif de bits. La spécification {:b} omet les zéros à gauche ; {:08b} complète à huit chiffres.
Chargement du contenu…