Représenter les entiers
Cours complet · NSI (première), chapitre 1 · première, spécialité numérique et sciences informatiques
Travailler ce chapitre sur Adloun Exercices corrigés de ce chapitre
« Toute machine informatique manipule une représentation des données dont l'unité minimale est le bit 0/1, ce qui permet d'unifier logique et calcul. » — Programme de NSI, rubrique Représentation des données : types et valeurs de base
Une machine ne sait pas ce qu'est le nombre douze. Elle sait mettre un fil sous tension ou l'y laisser, aimanter une plage de disque dans un sens ou dans l'autre, laisser passer un courant ou le bloquer. Deux états, rien de plus. Tout le reste — les entiers, les textes, les images, les sons — se construit sur ce choix binaire, et ce chapitre construit le premier étage : les entiers.
1.1 Compter dans une base quelconque
1.1.1 Ce que veut dire « écrire un nombre »
Écrire en base dix, c'est décider que le chiffre le plus à droite compte les unités, son voisin les dizaines, le suivant les centaines : . La base dix n'a rien de nécessaire ; elle vient de nos dix doigts. Remplaçons dix par un entier quelconque, et le mécanisme tient encore.
Soit un entier. Écrire l'entier en base , c'est le donner sous la forme
où les chiffres sont des entiers vérifiant , et où le chiffre de poids fort est non nul. On note cette écriture .
Pour tout entier et tout entier , il existe une écriture de en base , et une seule.
Ce théorème dit une chose simple, qu'il vaut la peine de voir écrite : dans une écriture positionnelle, ce n'est pas le chiffre seul qui compte, mais le chiffre et sa place.
Démonstration
Existence. Procédons par divisions euclidiennes successives. Posons , puis tant que , écrivons la division euclidienne avec . La suite est strictement décroissante d'entiers positifs — car dès que et — donc elle atteint en un nombre fini d'étapes. En remontant les substitutions, on obtient bien .
Unicité. Supposons deux écritures d'un même . Le chiffre est le reste de la division de par : il est donc déterminé de façon unique. En retranchant et en divisant par , on est ramené au même problème pour , strictement plus petit. Une descente finie conclut.
La démonstration ne fait pas que prouver : elle donne l'algorithme. Les restes successifs de divisions par sont les chiffres, lus de droite à gauche.
Capacité attendue
« Passer de la représentation d'une base dans une autre. »
1.1.2 De la base vers la base dix
Le sens facile. On applique la définition — mais pas naïvement. Écrire en calculant chaque puissance séparément coûte cher ; on préfère factoriser :
Cette réécriture s'appelle le schéma de Horner. Elle transforme le calcul en une seule boucle : partir de zéro, et pour chaque chiffre lu de gauche à droite, multiplier par puis ajouter le chiffre.
CHIFFRES = "0123456789ABCDEF"
def depuis_base(s, b):
"""Valeur de l'entier écrit s en base b (2 <= b <= 16).
Précondition : s ne contient que des chiffres valides en base b.
"""
n = 0
for c in s:
n = n * b + CHIFFRES.index(c)
return n
Calculons depuis_base("1011", 2). La variable n vaut successivement , puis , puis , puis , puis . On retrouve bien .
1.1.3 De la base dix vers la base
Le sens que donne la démonstration : diviser, garder le reste, recommencer sur le quotient. Les restes sortent dans l'ordre inverse de l'écriture, d'où la construction de la chaîne par la gauche.
def vers_base(n, b):
"""Écriture de l'entier n >= 0 en base b (2 <= b <= 16), sous forme de chaîne."""
if n == 0:
return "0"
s = ""
while n > 0:
s = CHIFFRES[n % b] + s
n = n // b
return s
le premier chiffre écrit : c'est pourquoi la fonction vers_base construit la chaîne par la gauche.</div>
Le cas est traité à part, et ce n'est pas un caprice : la boucle while n > 0 ne s'exécute jamais pour et renverrait la chaîne vide. C'est le genre d'oubli qu'un jeu de tests bien choisi attrape immédiatement — nous y reviendrons au chapitre 3.
Méthode : Vérifier une conversion sans refaire le calcul
Convertir dans un sens puis dans l'autre doit redonner le point de départ. Sur machine : depuis_base(vers_base(n, b), b) == n doit être vrai pour tout et tout admissibles. Cette propriété — deux fonctions inverses l'une de l'autre — vaut mieux qu'une vérification à la main, parce qu'elle se teste sur des milliers de valeurs.
1.2 Les trois bases qui comptent
Le programme privilégie explicitement les bases , et . Ce n'est pas un appauvrissement : ce sont les seules qu'on rencontre en pratique, et la troisième n'existe que pour servir la première.
1.2.1 La base deux
Un seul chiffre binaire s'appelle un bit — de l'anglais binary digit. Un groupe de huit bits forme un octet.
En base deux, les poids sont les puissances de deux : lire un octet ne demande donc aucun calcul, seulement une addition des poids des bits allumés.
1.2.2 La base seize, ou l'art d'abréger le binaire
L'écriture binaire est fidèle à la machine, mais illisible pour l'humain : un octet demande huit caractères. La base seize résout ce problème par une coïncidence heureuse, :
Pour convertir un nombre du binaire vers l'hexadécimal, il suffit de découper son écriture binaire en paquets de quatre bits en partant de la droite, et de remplacer chaque paquet par le chiffre hexadécimal correspondant. La conversion inverse remplace chaque chiffre hexadécimal par ses quatre bits.
Démonstration
Un paquet de quatre bits en position (en comptant les paquets depuis la droite, à partir de ) représente une valeur avec , pesant . La somme est donc exactement l'écriture en base seize, et les en sont les chiffres.
Huit caractères en binaire, deux en hexadécimal, pour la même information. C'est pourquoi les couleurs d'une page Web, les adresses mémoire et les empreintes de fichiers s'écrivent en hexadécimal — nous les retrouverons au chapitre 10.
Huit caractères deviennent deux, sans le moindre calcul.</div>
Python sait lire et écrire ces trois bases sans qu'on programme quoi que ce soit : bin(214) donne '0b11010110', hex(214) donne '0xd6', et int("D6", 16) redonne 214. Écrire soi-même vers_base n'était donc pas utile à la machine : c'était utile à vous.
Repère historique : Leibniz, 1703
L'arithmétique binaire n'est pas née avec les ordinateurs. En 1703, Leibniz publie dans les Mémoires de l'Académie royale des sciences une « Explication de l'arithmétique binaire » où il expose le calcul en base deux avec les seuls caractères et . Il y voit d'abord une curiosité philosophique et arithmétique ; il faudra attendre les premières machines, deux siècles et demi plus tard, pour que ce choix devienne celui de toutes les machines à calculer.
Le programme demande que ces repères soient « construits au fur et à mesure de la présentation des concepts » : vous en trouverez donc un dans chaque chapitre, jamais un chapitre entier.
1.3 Combien de bits faut-il ?
Capacité attendue
« Évaluer le nombre de bits nécessaires à l'écriture en base 2 d'un entier, de la somme ou du produit de deux nombres entiers. »
1.3.1 Pour un entier
Avec bits, on écrit exactement les entiers de à .
Démonstration
Chacun des bits vaut ou indépendamment des autres : il y a donc écritures possibles. Elles représentent des entiers deux à deux distincts par l'unicité de l'écriture en base deux, et la plus grande, , vaut .
Réciproquement, le nombre de bits de l'écriture de est le nombre de fois qu'on peut diviser par deux avant d'atteindre zéro, c'est-à-dire .
def nb_bits(n):
"""Nombre de bits de l'écriture binaire de n >= 0 (0 s'écrit sur 1 bit)."""
if n == 0:
return 1
k = 0
while n > 0:
n = n // 2
k = k + 1
return k
| Taille | Valeurs | Plus grand entier positif |
|---|---|---|
| 8 bits (1 octet) | ||
| 16 bits | ||
| 32 bits | ||
| 64 bits |
1.3.2 Pour une somme et pour un produit
Soient et deux entiers strictement positifs, écrits respectivement sur et bits. Alors :
- s'écrit sur au plus bits ;
- s'écrit sur ou bits.
Démonstration
Écrire sur bits signifie , et de même pour .
Somme. On a , donc tient sur bits.
Produit. D'une part , donc au plus bits. D'autre part , donc au moins bits.
demande sept bits alors que les deux opérandes en demandaient six — c'est le bit de plus annoncé par la proposition.</div>
Retenez l'ordre de grandeur, il servira souvent : additionner coûte un bit, multiplier coûte le total des deux. C'est la raison pour laquelle multiplier deux entiers de 32 bits demande un résultat de 64 bits — et pourquoi un langage qui ne le prévoit pas produit silencieusement un résultat faux.
1.4 Les entiers relatifs : le complément à deux
Rien dans ce qui précède ne permet d'écrire . Il faut donc une convention. La plus naïve consiste à réserver un bit pour le signe — et elle est mauvaise : elle donne deux zéros, et , et elle oblige le processeur à traiter l'addition différemment selon les signes. Les machines emploient un autre codage.
Capacité attendue
« Utiliser le complément à 2. »
Sur bits, on représente les entiers tels que de la façon suivante :
s'écrit 00000101. Pour , on code , soit 11111011. Le plus petit entier représentable, , se code 10000000 ; le plus grand, , se code 01111111.
ne trouve pas — qui n'existe pas ici — mais .</div>
Dans ce codage sur bits, un nombre est négatif si et seulement si son bit de poids fort vaut .
Démonstration
Si , l'écriture de tient sur bits : le bit de poids fort est nul. Si , alors : la valeur codée est au moins , donc son bit de poids fort vaut .
Si , et sont tous trois représentables sur bits, alors le codage de s'obtient en additionnant les codages de et de comme s'il s'agissait d'entiers positifs, puis en ignorant l'éventuelle retenue sortante.
C'est tout l'intérêt du codage, et il mérite d'être vu sur un cas : la retenue qui sort du dernier bit est simplement perdue, et cette perte est exactement ce qui réalise le calcul modulo .
Démonstration
Notons le codage de . Par définition, , et de même pour . Donc . Ignorer la retenue sortante, c'est précisément réduire modulo . Les deux membres étant dans , ils sont égaux.
C'est là tout l'intérêt du procédé, et la raison pour laquelle toutes les machines l'emploient : un seul circuit additionneur suffit, il n'a pas à savoir si ses opérandes sont positifs ou négatifs. La soustraction elle-même devient une addition.
def complement_a_2(x, n):
"""Codage de x sur n bits en complément à 2, sous forme de chaîne de n bits.
Précondition : -2**(n-1) <= x < 2**(n-1).
"""
assert -2**(n - 1) <= x < 2**(n - 1), "x n'est pas representable sur n bits"
if x < 0:
x = x + 2**n
return vers_base(x, 2).rjust(n, "0")
def depuis_complement_a_2(s):
"""Entier relatif codé par la chaîne de bits s, en complément à 2."""
v = depuis_base(s, 2)
if s[0] == "1":
v = v - 2**len(s)
return v
L'hypothèse du théorème compte : doit lui aussi être représentable. Sur 8 bits, n'est pas représentable, puisque le maximum est . Le calcul machine donne 11001000, dont le bit de poids fort vaut : la machine lit . Additionner deux nombres positifs a produit un négatif. Ce phénomène s'appelle un débordement, et il ne provoque aucune erreur : le résultat est simplement faux.
Hors programme : Ce que le programme n'exige pas
Le programme demande d'utiliser le complément à deux, pas d'en démontrer la théorie complète ni de connaître les jeux d'instructions des processeurs. De même, seules les bases , et sont au programme : la virtuosité en base sept ne sera demandée nulle part.
1.5 Et en Python ?
Tout ce chapitre décrit ce que fait une machine avec un nombre fixé de bits. Python, lui, manipule des entiers de taille arbitraire : il alloue autant de mémoire que nécessaire, et 2**1000 se calcule exactement. Vous ne rencontrerez donc pas de débordement en Python sur les entiers.
Ne concluez pas que le problème n'existe pas. Il existe partout ailleurs — dans un processeur, dans un fichier, dans un échange réseau, dans la plupart des autres langages — et il a causé des pannes célèbres. Python vous protège ; il ne vous dispense pas de comprendre.
Piste de projet : Un visualiseur de représentations
Écrire un programme qui, pour un entier saisi par l'utilisateur et une taille en bits choisie, affiche côte à côte : son écriture décimale, binaire, hexadécimale, son codage en complément à deux, le nombre de bits strictement nécessaires, et un avertissement s'il n'est pas représentable. Une extension naturelle : animer l'addition bit à bit, avec les retenues, et signaler visuellement le débordement.
Ce sujet mobilise les deux chapitres suivants (interface, jeux de tests) et se prête bien à un groupe de deux.