3-01. Représentation de l’information
Dans ce chapitre, nous allons découvrir comment les données sont stockées et manipulées dans la mémoire d’un ordinateur. Tout commence par la base du numérique : les nombres, du binaire de la machine à l’hexadécimal des développeurs.
Entiers positifs
- Comprendre le principe de la numération de position.
- Passer d’une base à une autre (bases 2, 10 et 16).
- Comprendre les notions de bit, d’octet et de quartet.
- Manipuler les différentes bases de numération en Python.
Pourquoi plusieurs bases de numération ?
Au quotidien, on compte en base 10 (système décimal). Pourquoi ? Tout simplement parce qu’on a 10 doigts ! On utilise donc 10 symboles (les chiffres de 0 à 9) et, dès qu’on dépasse 9, on crée une nouvelle « retenue » (les dizaines, les centaines, etc.).
Mais les ordinateurs n’ont pas de doigts. Ils sont constitués de milliards de micro-composants électroniques (les transistors) qui ne connaissent que deux états physiques :
- Passant : noté
1(allumé) ; - Bloquant : noté
0(éteint).
L’ordinateur compte donc naturellement en base 2 (système binaire). L’unité minimale d’information en informatique s’appelle un bit (contraction de binary digit, soit « chiffre binaire »). Un bit ne peut valoir que 0 ou 1.
💡 Bit vs octet :
- Un bit est la plus petite unité (
0ou1). - Un octet (byte en anglais) est un regroupement de 8 bits (par exemple
10110100). C’est l’unité de mesure standard de la mémoire informatique.
💡 Le saviez-vous ?
La base 10 n’est pas la seule base humaine ! Certaines civilisations de l’Antiquité (comme les Sumériens) utilisaient la base 12 (système duodécimal). En effet, si vous utilisez le pouce d’une main comme pointeur, vous pouvez compter les 12 phalanges des 4 autres doigts ! C’est de cet héritage antique que viennent nos journées de 24 heures (2 × 12 heures).
Travailler uniquement avec des suites de 0 et de 1 (comme 110101101011) devient très vite illisible pour nous, humains. C’est pour ça que les informaticiens utilisent la base 16 (système hexadécimal) comme une écriture « condensée » et pratique du binaire.
Comprendre la numération de position
Avant d’étudier le binaire et l’hexadécimal, décomposons la manière dont nous écrivons les nombres en base 10. C’est ce qu’on appelle la numération de position : la valeur d’un chiffre dépend de sa position dans le nombre.
Prenons le nombre 2026 en base 10 :
| Position (rang) | 3 | 2 | 1 | 0 |
|---|---|---|---|---|
| Poids de la colonne | 103 = 1000 | 102 = 100 | 101 = 10 | 100 = 1 |
| Chiffre | 2 | 0 | 2 | 6 |
Ce nombre vaut donc : 2×1000 + 0×100 + 2×10 + 6×1 = 2026.
🖐️ Dans n’importe quelle base $*b*$ :
- On dispose de $*b*$ symboles différents (de 0 à $*b*$-1).
- Les poids des colonnes sont les puissances successives de la base : $*b*$0, $*b*$1, $*b*$2, $*b*$3, etc., en partant de la droite.
La base 2 : le binaire
En base 2, on n’a que deux symboles : 0 et 1. Les poids des colonnes sont les puissances de 2 : 20 = 1, 21 = 2, 22 = 4, 23 = 8, 24 = 16, 25 = 32, 26 = 64, 27 = 128, etc.
💡Il est très utile d’apprendre par cœur les premières puissances de 2 : 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024… (chaque nombre est simplement le double du précédent !).
A. Conversion du binaire vers le décimal
Pour connaître la valeur en décimal d’un nombre binaire, il suffit d’additionner les poids des colonnes qui contiennent un 1.
Exemple 1 : Convertir le nombre binaire (1011)2 en décimal.
| Puissance de 2 | 23 = 8 | 22 = 4 | 21 = 2 | 20 = 1 |
|---|---|---|---|---|
| Bit | 1 | 0 | 1 | 1 |
| Calcul | 1×8 | 0×4 | 1×2 | 1×1 |
Calcul : 8 + 0 + 2 + 1 = 11. Donc (1011)2 = (11)10.
Exemple 2 : Convertir l’octet (11001010)2 en décimal.
| Poids | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
|---|---|---|---|---|---|---|---|---|
| Bit | 1 | 1 | 0 | 0 | 1 | 0 | 1 | 0 |
Calcul : 128 + 64 + 8 + 2 = 202. Donc (11001010)2 = (202)10.
B. Conversion du décimal vers le binaire
Il existe deux méthodes pour effectuer cette conversion.
Méthode 1 : La méthode des soustractions
C’est la méthode la plus rapide de tête pour les petits nombres.
Soit à convertir 45 en binaire :
-
Quelle est la plus grande puissance de 2 inférieure ou égale à 45 ? C’est 32. On met un
1sous 32.
Il reste 45 – 32 = 13. - Dans 13, peut-on caser 16 ? Non → on met un
0sous 16. -
Dans 13, peut-on caser 8 ? Oui → on met un
1sous 8.
Il reste 13 – 8 = 5. -
Dans 5, peut-on caser 4 ? Oui → on met un
1sous 4.
Il reste 5 – 4 = 1. - Dans 1, peut-on caser 2 ? Non → on met un
0sous 2. -
Dans 1, peut-on caser 1 ? Oui → on met un
1sous 1.
Il reste 0. Fin !
Résultat : 45 = 32 + 8 + 4 + 1 ⇒ (45)10 = (101101)2.
Méthode 2 : Les divisions successives par 2
C’est la méthode systématique, idéale pour les algorithmes ou les grands nombres.
On divise le nombre par 2 répétitivement et on conserve les restes de la division euclidienne jusqu’à obtenir un quotient égal à 0. On lit ensuite les restes de bas en haut (du dernier au premier).
Convertissons 45 avec cette méthode :
- 45 ÷ 2 = 22, reste 1 (bit de poids faible)
- 22 ÷ 2 = 11, reste 0
- 11 ÷ 2 = 5, reste 1
- 5 ÷ 2 = 2, reste 1
- 2 ÷ 2 = 1, reste 0
- 1 ÷ 2 = 0, reste 1 (bit de poids fort)
En lisant les restes du bas vers le haut, on retrouve bien : (101101)2.
Conversions binaire ↔ décimal
1. Quel est le plus grand nombre entier en base 10 qui puisse être exprimé en un octet ?
2. Convertir les nombres binaires suivants en base 10 (décimal) :
- a = (00001101)2
- b = (10000001)2
- c = (11111111)2
3. Convertir les nombres décimaux suivants en binaire sur un octet (8 bits) :
- x = 25
- y = 100
- z = 180
Correction
1. 28 + 27 + 26 + 24 + 23 + 22 + 21 + 20 = 128 + 64 + 32 + 16 + 8 + 4 + 2 + 1 = 255
2. Binaire vers décimal :
- (00001101)2 = 8 + 4 + 1 = 13
- (10000001)2 = 128 + 1 = 129
- (11111111)2 = 128 + 64 + 32 + 16 + 8 + 4 + 2 + 1 = 255 (valeur maximale d’un octet !)
3. Décimal vers binaire :
- 25 = 16 + 8 + 1 → (00011001)2
- 100 = 64 + 32 + 4 → (01100100)2
- 180 = 128 + 32 + 16 + 4 → (10110100)2
La base 16 : l’hexadécimal
En base 16, il nous faut 16 symboles. Les chiffres de 0 à 9 ne suffisent plus ! On utilise donc les lettres de A à F pour représenter les valeurs de 10 à 15 :
| Décimal | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Hexadécimal | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F |
Le lien entre binaire et hexadécimal
Puisque 16 = 24, un chiffre hexadécimal correspond exactement à un paquet de 4 bits.
💡 Un octet (8 bits) s’écrit donc toujours très simplement avec exactement 2 chiffres hexadécimaux ! Par exemple, 11111111 en binaire vaut FF en hexadécimal.
A. Conversion binaire ↔ hexadécimal
Pour convertir un nombre binaire en hexadécimal :
- On découpe le nombre binaire par paquets de 4 bits en partant de la droite.
- On convertit chaque paquet de 4 bits en son chiffre hexadécimal correspondant.
Exemple : Convertir (11010110)2 en hexadécimal.
- On découpe en 2 quartets :
1101et0110. - (1101)2 = 8 + 4 + 1 = 13 ⇒ en hexadécimal, c’est D.
- (0110)2 = 4 + 2 = 6 ⇒ en hexadécimal, c’est 6.
Résultat : (11010110)2 = (D6)16.
B. Convertir de l’hexadécimal vers le décimal
Les poids des colonnes en hexadécimal sont les puissances de 16 : 160 = 1, 161 = 16, 162 = 256, etc.
Exemple : Convertir (2F)16 en décimal.
- Le chiffre
2est au rang 1 (poids 16) : 2×16 = 32. - Le chiffre
F(qui vaut 15) est au rang 0 (poids 1) : 15×1 = 15.
Résultat : 32 + 15 = 47. Donc (2F)16 = (47)10.
Hexadécimal et couleurs
En informatique, les couleurs sont représentées en code hexadécimal sous la forme #RRGGBB, où :
RRest la quantité de rouge (sur un octet, de 00 à FF) ;GGest la quantité de vert (sur un octet, de 00 à FF) ;BBest la quantité de bleu (sur un octet, de 00 à FF).
1. Convertir les paires hexadécimales suivantes en valeurs décimales (de 0 à 255) :
- 00
- 80
- FF
- A0
2. Soit une couleur définie par #FF8000 (du orange). Donnez ses composantes Rouge, Vert et Bleu sous forme de valeurs décimales compris entre 0 et 255.
3. Convertir le nombre binaire (110110101111)2 directement en hexadécimal en utilisant la méthode des quartets.
Correction
1. Hexadécimal vers décimal :
00= 0 × 16 + 0 = 080= 8 × 16 + 0 = 128FF= 15 × 16 + 15 = 255A0= 10 × 16 + 0 = 160
2. Couleur #FF8000 :
- Rouge (
FF) = 255 - Vert (
80) = 128 - Bleu (
00) = 0
3. Binaire vers hexadécimal par quartets :
On découpe 110110101111 en quartets : 1101.1010.1111
- (
1101)2 = 8 + 4 + 1 = 13 → D - (
1010)2 = 8 + 2 = 10 → A - (
1111)2 = 8 + 4 + 2 + 1 = 15 → F
Résultat : (DAF)16 ou 0xDAF.
Les bases de numération en Python
Python sait manipuler nativement les nombres écrits dans différentes bases. Par défaut, tous les nombres affichés par Python le sont en base 10, mais on peut lui spécifier la base lors de la saisie d’une valeur en utilisant un préfixe :
- Préfixe
0bpour le binaire (ex:0b1011) - Préfixe
0xpour l’hexadécimal (ex:0xA3)
a = 0b1011 # Saisie en binaire (vaut 11 en décimal)
b = 0xA3 # Saisie en hexadécimal (vaut 163 en décimal)
print(a) # Affiche 11 (Python convertit automatiquement en décimal)
print(b) # Affiche 163
print(a + b) # Affiche 174 (11 + 163)
Fonctions de conversion en Python
Python fournit des fonctions intégrées pour convertir un entier vers une chaîne de caractères représentée dans une autre base, ou vice-versa :
| Fonction / Instruction | Rôle | Exemple | Résultat |
|---|---|---|---|
bin(n) |
Convertit un entier n en chaîne binaire |
bin(13) |
"0b1101" |
hex(n) |
Convertit un entier n en chaîne hexadécimale |
hex(255) |
"0xff" |
int(texte, base) |
Convertit du texte exprimé dans une base vers un entier décimal |
int("1011", 2)int("A3", 16)
|
11163
|
# Passer du décimal vers le binaire/hexa :
nombre = 42
print(bin(nombre)) # "0b101010"
print(hex(nombre)) # "0x2a"
# Passer d’une chaîne binaire ou hexa vers un nombre entier :
valeur_bin = int("1101", 2) # 13
valeur_hex = int("FF", 16) # 255
print(valeur_bin + valeur_hex) # 268
Manipulations en Python
1. Expliquez ce qu’affichent les instructions suivantes :
a = 0b101
b = 0x10
print(a)
print(b)
print(a + b)
2. Écrivez une ou deux lignes de code Python qui prennnent la chaîne de caractères "10110" (en binaire) et affiche son équivalent sous forme de chaîne de caractères en hexadécimal (indice : combinez hex() et int()).
Correction
1. Résultat des instructions
a = 0b101vaut 4 + 1 = 5 en décimal.b = 0x10vaut 1×16 + 0 = 16 en décimal.print(a + b)affiche21(car 5 + 16 = 21).
2. Conversion binaire ⟶ hexadécimal
print(hex(int("10110", 2))) # Affiche ’0x16’
Explication : int("10110", 2) convertit la chaîne binaire en l’entier décimal 22, puis hex(22) renvoie la chaîne hexadécimale ’0x16’.
Entiers relatifs
- Évaluer le nombre de bits nécessaires à l’écriture en binaire d’un entier, d’une somme et d’un produit.
- Comprendre le codage des entiers relatifs avec le complément à 2.
- Connaître les tailles courantes d’entiers (8, 16, 32 et 64 bits) et la spécificité de Python.
Taille des entiers en mémoire (champs de bits fixes)
Dans la section précédente, nous avons écrit des nombres binaires avec autant de bits que nécessaire. Mais dans un ordinateur réel, la mémoire physique est organisée en « cases » de tailles fixes. Les tailles les plus fréquentes sont :
- 8 bits (1 octet) ;
- 16 bits (2 octets) ;
- 32 bits (4 octets) ;
- 64 bits (8 octets).
Quand on réserve un emplacement mémoire de $*n*$ bits pour un entier non signé (positif ou nul), on dispose de 2$*n*$ combinaisons possibles, permettant de coder tous les entiers de 0 à 2$*n*$ – 1.
| Taille | Nombre de combinaisons (2$*n*$) | Intervalle (non signé) |
|---|---|---|
| 8 bits | 28 = 256 | 0 à 255 |
| 16 bits | 216 = 65 536 | 0 à 65 535 |
| 32 bits | 232 ≈ 4,29 × 109 | 0 à 4 294 967 295 |
| 64 bits | 264 ≈ 1,84 × 1019 | 0 à 18 446 744 073 709 551 615 |
💡 Anectode de jeu vidéo (L’overflow) :
Dans les jeux rétro (comme Pac-Man ou Pokémon), le niveau ou les statistiques étaient souvent codés sur 8 bits (max 255). Au niveau 256 de Pac-Man, 255 + 1 provoquait un dépassement de mémoire (overflow), remettant le compteur à 0 et corrompant l’affichage du jeu !
Évaluer le nombre de bits nécessaires
A. Pour un entier $*N*$
Pour coder un entier positif $*N*$ en binaire, il faut trouver le plus petit nombre de bits $*k*$ tel que $*N*$ < 2$*k*$. En d’autres termes, le nombre de bits $*k*$ est tel que 2$*k*$-1 ≤ $*N*$ < 2$*k*$.
Exemple : Pour coder $*N*$ = 200 :
- 27 = 128 (trop petit)
- 28 = 256 (suffisant car 200 < 256)
Il faut donc 8 bits pour représenter 200.
B. Pour une somme ($*A*$ + $*B*$)
Si un entier $*A*$ nécessite $*k*$ bits et un entier $*B*$ nécessite $*p*$ bits (avec $*k*$ ≥ $*p*$), la somme $*A*$ + $*B*$ nécessite au plus $*k*$ + 1 bits (un bit de plus pour gérer la retenue finale).
Exemple : $*A*$ = 200 (8 bits) et $*B*$ = 150 (8 bits). $*A*$ + $*B*$ = 350. Comme 350 < 29 = 512, il faut 9 bits.
C. Pour un produit ($*A*$ × $*B*$)
Si un entier $*A*$ s’écrit sur $*k*$ bits et $*B*$ sur $*p*$ bits, leur produit $*A*$ × $*B*$ nécessite au plus $*k*$ + $*p*$ bits.
Exemple : $*A*$ = 15 (11112 → 4 bits) et $*B*$ = 12 (11002 → 4 bits). $*A*$ × $*B*$ = 180. Comme 180 < 28 = 256, il faut 4 + 4 = 8 bits.
Le cas particulier de Python : entiers de taille arbitraire
Dans la plupart des langages de programmation traditionnels (C, C++, Java), les variables ont une taille fixe dans la mémoire (32 ou 64 bits). Si le résultat d’un calcul dépasse cette taille, le nombre recommence à zéro ou provoque une erreur.
Python est différent ! Il gère automatiquement les entiers de taille arbitraire. Tant que votre ordinateur a de la mémoire RAM disponible, Python agrandit la taille mémoire du nombre au fur et à mesure qu’il grandit. On peut donc calculer 21000 sans aucun problème de dépassement !
Taille en bits d’une somme et d’un produit
1. Quel est le nombre minimal de bits nécessaires pour représenter le nombre décimal 150 ?
2. On considère deux entiers $*A*$ = 100 et $*B*$ = 200.
- Sur combien de bits faut-il coder $*A*$ et $*B*$ individuellement ?
- Sur combien de bits faut-il coder la somme $*A*$ + $*B*$ ?
- Sur combien de bits faut-il coder le produit $*A*$ × $*B*$ ?
Correction
1. 150 < 28 = 256. Il faut 8 bits (1 octet).
2.
- $*A*$ = 100 < 128 → 7 bits. $*B*$ = 200 < 256 → 8 bits.
- $*A*$ + $*B*$ = 300. 300 < 29 = 512 → 9 bits.
- $*A*$ × $*B*$ = 20 000. 20 000 < 215 = 32 768 → 15 bits (au plus 7 + 8 = 15 bits).
Représentation des entiers négatifs : le complément à 2
Un ordinateur ne dispose ni d’un signe « – », ni d’un signe « + ». Il ne connaît que des 0 et des 1. Comment représenter un nombre négatif comme –5 dans une case mémoire de $*n*$ bits ?
Pourquoi la méthode « naïve » ne marche pas
L’idée la plus simple consisterait à réserver le premier bit (celui tout à gauche) pour le signe (0 pour +, 1 pour –). Mais cette méthode pose deux gros problèmes :
- Elle crée deux représentations pour zéro : un « +0 » et un « –0 », ce qui fait gaspiller une combinaison.
- Les opérations mathématiques de base ne fonctionnent plus ! Additionner +5 et –5 ne donnerait pas 0.
La solution universelle : le complément à 2
En informatique, les entiers signés (positifs ou négatifs) sont codés selon la méthode du complément à 2.
Dans un codage en complément à 2 sur $*n*$ bits, le premier bit à gauche (bit de poids fort) a un rôle particulier :
- S’il vaut
0, le nombre est positif ou nul. - S’il vaut
1, le nombre est négatif. Son poids n’est pas +2$*n*$-1, mais –2$*n*$-1 !
Quelques exemples de nombres relatifs 8 bits codés en complément à 2 :
| ±128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 | base 10 |
|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 (plus petit positif) |
| 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 127 (plus grand positif) |
| 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | -128 (plus petit négatif) |
| 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | -1 (plus grand négatif) |
⚙️ Algorithme pour coder un entier négatif –$*X*$ sur $*n*$ bits :
- Écrire la valeur absolue +$*X*$ en binaire sur $*n*$ bits (en rajoutant les
0inutiles à gauche). - Inverser tous les bits : changer tous les
0en1et tous les1en0. (C’est le complément à 1). - Ajouter 1 au résultat binaire obtenu. (C’est le complément à 2).
Exemple pas à pas : Coder le nombre –5 sur 8 bits (1 octet).
- On écrit +5 sur 8 bits :
0000 0101 - On inverse tous les bits :
1111 1010 - On ajoute 1 :
1111 1010+ 1 =1111 1011
Ainsi, en complément à 2 sur 8 bits, –5 s’écrit 1111 1011.
Mais pourquoi c’est si compliqué ? On est à 127, et quand on ajoute 1, on passe à -128 ! 😮
Oui, parce que si on essaie de faire 127 + 1 (c’est-à-dire avoir 128), on se retrouve en dehors de l’intervalle possible pour les entiers relatifs sur 8 bits, c’est à dire l’intervalle [-128 ; 127]. Donc on revient au nombre le plus petit de l’intervalle, soit -128.
Mais ce codage présente un gros avantage :
💡 Faire la somme d’un nombre et de son opposé donne bien zéro
# 0000 0001 (+1)
+ 1111 1111 (-1)
–––––––––––
1 0000 0000
Le bit le plus à gauche (le 9e bit) est ignoré, car on travaille sur 8 bits.
# 0000 0100 (+4)
+ 1111 1100 (-4)
–––––––––––
1 0000 0000
Codage en complément à 2 sur 8 bits
1. Donner le codage binaire en complément à 2 sur 8 bits des entiers suivants :
- $*A*$ = +18
- $*B*$ = –18
- $*C*$ = –1
- $*D*$ = –128
2. Retrouver la valeur décimale des deux octets signés suivants (codés en complément à 2) :
E = 0101 0000F = 1111 0000
Correction
1. Codage sur 8 bits :
- $*A*$ = +18 : 18 = 16 + 2 →
0001 0010(positif, donc écriture directe). - $*B*$ = –18 :
1. +18 →0001 0010
2. Inversion →1110 1101
3. +1 →1110 1110 - $*C*$ = –1 :
1. +1 →0000 0001
2. Inversion →1111 1110
3. +1 →1111 1111(tous les bits à 1 valent –1). - $*D*$ = –128 : C’est la limite minimale sur 8 bits signés →
1000 0000(car –128 + 0 = –128).
2. Décodage d’octets signés :
E = 0101 0000: Le premier bit est0, donc c’est un nombre positif.
Calcul : 64 + 16 = +80.F = 1111 0000: Le premier bit est1, donc c’est un nombre négatif.
Calcul avec poids du bit fort : –128 + 64 + 32 + 16 = –16.
Autre méthode : Inverser les bits (0000 1111) + 1 (0001 0000= 16), donc le nombre vaut –16.
Nombres réels et nombres flottants
- Comprendre le principe de la représentation binaire des réels (puissances négatives de 2).
- Calculer la représentation binaire de réels simples (0,5 ; 0,25 ; 0,75…).
- Comprendre pourquoi certains nombres réels comme 0,1 n’ont pas d’écriture binaire exacte.
- Comprendre les conséquences pratiques en programmation (erreurs d’arrondi et comparaison de flottants).
L’infini dans une mémoire finie
Pour les entiers, les choses sont simples : entre 0 et 10, il n’y a qu’un nombre fini d’entiers (11 exactement). Mais pour les nombres réels (les nombres à virgule), c’est une autre histoire : entre 0 et 1, il existe une infinité de nombres réels (0,1 ; 0,15 ; 0,0001 ;…)
Un ordinateur possédant une mémoire finie, il est physiquement impossible de stocker toutes les valeurs réelles possibles. L’ordinateur va donc devoir stocker la plupart de ces nombres sous forme approximative. On appelle ces nombres des flottants (floats en anglais).
La virgule en binaire (puissances négatives de 2)
En décimal, les chiffres situés après la virgule représentent les dixièmes (10–1 = 0,1), les centièmes (10–2 = 0,01), les millièmes (10–3 = 0,001), etc.
En binaire, c’est le même principe ! Les chiffres après la virgule correspondent aux puissances négatives de 2 :
| Rang après la virgule | 1 | 2 | 3 | 4 |
|---|---|---|---|---|
| Puissance de 2 | 2–1 | 2–2 | 2–3 | 2–4 |
| Valeur décimale | 0,5 (1/2) | 0,25 (1/4) | 0,125 (1/8) | 0,0625 (1/16) |
Pour trouver la valeur d’un nombre binaire à virgule, on additionne simplement ses poids :
- (0,1)2 = 1 × 0,5 = 0,5
- (0,01)2 = 0 × 0,5 + 1 × 0,25 = 0,25
- (0,11)2 = 0,5 + 0,25 = 0,75
- (10,101)2 = 2 + 0,5 + 0,125 = 2,625
Les nombres « infinis »
En base 10, nous savons tous que la fraction 1/3 ne « tombe pas juste » : 1/3 = 0,333333… avec une infinité de 3. On ne peut pas l’écrire exactement avec un nombre fini de chiffres décimaux.
En binaire, c’est exactement la même chose pour certaines fractions. Un nombre décimal ne possède une écriture binaire exacte que s’il s’exprime comme une somme finie de puissances négatives de 2.
Prenons le nombre 0,1 (c’est-à-dire 1/10). Si on essaie de le convertir en binaire :
(0,1)10 = (0,00011001100110011…)2 (la suite 0011 se répète à l’infini !)
Le nombre 0,1 (pourtant tout simple en décimal) a une écriture infinie en binaire ! Comme l’ordinateur ne peut pas stocker une infinité de bits, il doit couper le nombre.
Résultat : 0,1 n’est pas stocké exactement comme 0,1 dans la mémoire de votre ordinateur !
Comment la mémoire stocke-t-elle un « flottant » ?
Je ne comprends pas, commet l’ordinateur peut écrire 0,001101 ? Vous avez dit qu’il ne peut pas stocker la virgule… 🤔.
C’est vrai ! Dans la mémoire de l’ordinateur, il n’existe pas de caractère « point » ou « virgule », ni de signe « – ». Il n’y a que des 0 et des 1. Pour stocker un nombre réel sur 64 bits (norme courante des ordinateurs), la mémoire utilise la notation scientifique binaire (par exemple 1,101-10) et découpe la case mémoire en 3 zones fixes :
| Signe (1 bit) | Exposant (11 bits) | Mantisse (52 bits) |
|---|---|---|
| 0 = Positif 1 = Négatif |
Déplace la virgule vers la gauche ou la droite selon son signe. | Les chiffres significatifs situés après le « 1, » (implicite) |
L’ordinateur n’a donc pas besoin de stocker le symbole de la virgule : la position de la virgule est calculée dynamiquement grâce à l’exposant (elle « flotte », d’où le nom de flottant). Et si la suite de bits est infinie (comme pour 0,1), la mantisse se remplit jusqu’au 52e bit et coupe sauvagement le reste, créant une infime erreur d’arrondi.
Conversion de réels simples en binaire
1. Convertir les nombres décimaux suivants en écriture binaire à virgule :
- A = 0,5
- B = 0,25
- C = 0,875
- D = 3,5
2. Donnez la valeur décimale exacte du nombre binaire (11,01)2.
Correction
1. Conversions en binaire :
- A = 0,5 : 0,5 = 2-1 ⟶
(0,1)₂ - B = 0,25 : 0,25 = 2-2 ⟶
(0,01)₂ - C = 0,875 : 0,875 = 0,5 + 0,25 + 0,125 = 2-1 + 2-2 + 2-3 ⟶
(0,111)₂ - D = 3,5 : 3 = (11)2 et 0,5 = (0,1)2 ⟶
(11,1)₂
2. Valeur décimale de (11,01)2 :
Partie entière : (11)2 = 2 + 1 = 3.
Partie fractionnaire : (0,01)2 = 0 × 0,5 + 1 × 0,25 = 0,25.
Résultat : 3 + 0,25 = 3,25.
Les conséquences concrètes en Python
Cette légère imprécision a des conséquences directes très célèbres en programmation. Ouvrez une console Python et tapez :
>>> 0.1 + 0.2
0.30000000000000004
>>> 0.1 + 0.2 == 0.3
False
Puisque 0,1 et 0,2 sont stockés avec de minuscules erreurs d’arrondi en binaire, leur somme donne 0.30000000000000004 et non pas exactement 0.3 !
⛔ Ne testez JAMAIS l’égalité stricte (==) entre deux nombres flottants ! En raison des erreurs d’arrondi, deux calculs censés donner le même résultat réel peuvent différer d’une infime fraction et rendre le test faux.
Comment comparer correctement deux flottants en Python ?
Pour vérifier si deux flottants a et b sont "égaux", on teste si la valeur absolue de leur différence est extrêmement petite (inférieure à un seuil de tolérance $*\epsilon*$, par exemple 10–9) :
a = 0.1 + 0.2
b = 0.3
# ❌ Mauvaise méthode :
if a == b:
print("Égaux") # Ne s’exécutera PAS !
# ✅ Bonne méthode (test de tolérance) :
if abs(a - b) < 1e-9:
print("Les nombres sont égaux à la tolérance près !")
# ✅ Variante avec le module math de Python :
import math
if math.isclose(a, b):
print("Les nombres sont très proches !")
Boucle infinie avec les flottants
Observez attentivement le programme Python suivant :
x = 0.0
while x != 1.0:
print(x)
x = x + 0.1
1. Pourquoi ce programme risque-t-il de tourner en boucle infinie au lieu de s’arrêter lorsque x atteint 1.0 ?
2. Proposez une modification simple de la condition du while pour sécuriser l’arrêt de la boucle.
Correction
1. Explication du piège :
À cause des erreurs d’arrondi répétées lors des additions de 0.1 en binaire, la variable x ne va jamais valoir exactement 1.0 (elle va par exemple valoir 0.9999999999999999 puis sauter directement à 1.0999999999999999). La condition x != 1.0 restera donc toujours vraie (True) et la boucle ne s’arrêtera jamais !
2. Correction du code :
Au lieu de tester l’inégalité stricte avec !=, on utilise une inégalité < :
x = 0.0
while x < 1.0: # Se termine dès que x dépasse 1.0
print(x)
x = x + 0.1
Valeurs et expressions booléennes
- Connaître les deux valeurs booléennes (
True/False, 1 / 0) et le typebool. - Dresser la table de vérité des opérateurs fondamentaux (
not,and,or) et du OU exclusif (xor). - Évaluer des expressions booléennes combinées et maîtriser les règles de priorité.
- Comprendre et exploiter le principe d’évaluation séquentielle (« paresseuse » ou court-circuit).
Qu’est-ce qu’un booléen ?
En informatique, une variable booléenne ne peut prendre que deux états possibles :
- Vrai (noté
Trueen Python, ou1en logique binaire) ; - Faux (noté
Falseen Python, ou0en logique binaire).
Ce concept fondamental repose sur l’algèbre créée au XIXe siècle par le mathématicien britannique George Boole. En Python, ce type de donnée s’appelle bool. Puisqu’il n’y a que deux états possibles, une valeur booléenne tient théoriquement sur 1 bit.
💡 Le saviez-vous ?
En Python, le type bool est en réalité un sous-type de int : True a pour valeur numérique 1 et False vaut 0. On peut d’ailleurs écrire True + True, ce qui donne 2 !
Les opérateurs logiques fondamentaux
Pour combiner des valeurs booléennes, on utilise des opérateurs logiques. Leurs règles sont synthétisées dans une table de vérité listant tous les cas possibles.
1. La négation : not (NON)
Inverse la valeur booléenne.
| A | not A |
|---|---|
False (0) |
True (1) |
True (1) |
False (0) |
2. La conjonction : and (ET)
L’expression est vraie uniquement si les deux conditions sont vraies.
| A | B | A and B |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 0 |
| 1 | 0 | 0 |
| 1 | 1 | 1 |
3. La disjonction : or (OU inclusif)
L’expression est vraie si au moins l’une des deux conditions est vraie.
| A | B | A or B |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 1 |
4. Le OU exclusif : xor (^ en Python)
Vrai si l’une OU l’autre est vraie, mais pas les deux à la fois.
| A | B | A ^ B |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
Générer des booléens : les comparateurs
En programmation, les booléens sont le plus souvent le résultat de comparaisons entre variables :
| Opérateur | Signification | Exemple | Résultat |
|---|---|---|---|
== |
Égal à | 5 == 5 |
True |
!= |
Différent de | 4 != 4 |
False |
< / <= |
Strictement inférieur / Inférieur ou égal | 3 <= 7 |
True |
> / >= |
Strictement supérieur / Supérieur ou égal | 10 > 12 |
False |
⛔ Ne confondez pas = et == !
a = 5est une affectation (on stocke la valeur 5 dans la variablea).a == 5est un test d’égalité (qui renvoie le booléenTrueouFalse).
Évaluation des expressions composées et priorités
Comme pour les opérations arithmétiques (où la multiplication est prioritaire sur l’addition), les opérateurs logiques ont un ordre de priorité strict :
- Les parenthèses
( )(priorité absolue) ; - L’opérateur
not; - L’opérateur
and; - L’opérateur
or.
L’évaluation séquentielle
En Python (et dans la plupart des langages modernes), les expressions logiques sont évaluées de gauche à droite avec un mécanisme d’évaluation séquentielle (dite « paresseuse » ou court-circuit) :
- Dans
False and <expression>: comme le premier membre est faux, l’expression globale sera forcément fausse. Python n’évalue même pas la seconde expression. - Dans
True or <expression>: comme le premier membre est vrai, l’expression sera forcément vraie. Python ignore également la suite.
💡 Application très utile : la protection contre les erreurs
L’évaluation court-circuit permet de placer une condition de garde avant une opération risquée (comme une division par zéro ou un accès hors limite) :
n = 0
# Si n == 0, (n != 0) vaut False : Python s’arrête et n’exécute pas (10 / n) !
if n != 0 and (10 / n) > 2:
print("Condition validée")
else:
print("Aucune erreur ZeroDivisionError n’a été levée !")
Évaluation manuelle d’expressions booléennes
Soient les variables suivantes : x = 7, y = 12, z = 0.
Déterminez la valeur booléenne (True ou False) de chacune des expressions suivantes :
x < 10 and y > 10x == 5 or y == 12not (x > y) and z == 0x > 5 and not (y < 15 or z == 0)
Correction
(7 < 10) and (12 > 10)⟶True and True⟶True(7 == 5) or (12 == 12)⟶False or True⟶Truenot (7 > 12) and (0 == 0)⟶not (False) and True⟶True and True⟶True(7 > 5) and not (12 < 15 or 0 == 0)⟶True and not (True or True)⟶True and not (True)⟶True and False⟶False
Système d’alarme et table de vérité
Un système d’alarme déclenche une sirène S selon l’état de trois capteurs booléens :
- A : L’alarme est armée (1 = activée, 0 = désactivée).
- D : Détecteur de mouvement (1 = présence détectée, 0 = rien).
- F : Détecteur d’ouverture de fenêtre (1 = fenêtre ouverte, 0 = fermée).
La sirène retentit (S = 1) si l’alarme est armée ET qu’un mouvement est détecté OU qu’une fenêtre est ouverte.
1. Écrire l’expression logique de S en fonction de A, D et F.
2. Compléter la table de vérité de S.
Correction
1. Expression logique :
S = A and (D or F) (les parenthèses sont obligatoires pour que le « et » s’applique aux deux types d’intrusion).
2. Table de vérité :
| A | D | F | D or F |
S = A and (D or F) |
|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 |
| 0 | 0 | 1 | 1 | 0 |
| 0 | 1 | 0 | 1 | 0 |
| 0 | 1 | 1 | 1 | 0 |
| 1 | 0 | 0 | 0 | 0 |
| 1 | 0 | 1 | 1 | 1 |
| 1 | 1 | 0 | 1 | 1 |
| 1 | 1 | 1 | 1 | 1 |
Représentation du texte et chaînes de caractères
- Comprendre la notion d’encodage (associer un code numérique à chaque symbole ou caractère).
- Connaître les caractéristiques et limites des encodages ASCII et ISO-8859-1 (Latin-1).
- Comprendre le principe universel du standard Unicode et du codage UTF-8.
- Manipuler les fonctions Python de conversion de caractères (
ord()etchr()).
Du binaire au caractère : l’encodage
Un ordinateur ne stocke que des 0 et des 1. Pour manipuler du texte (lettres, signes de ponctuation, espaces, émojis), il faut établir une convention qui associe chaque symbole à un nombre entier binaire : c’est ce qu’on appelle un encodage de caractères.
Une chaîne de caractères (type str en Python) est simplement une suite ordonnée de caractères stockés en mémoire selon un encodage donné.
1. L’ASCII : la norme historique (7 bits)
Créé en 1963 aux États-Unis, le standard ASCII (American Standard Code for Information Interchange) est la base historique de l’informatique moderne.
- Il utilise 7 bits par caractère, permettant de coder 27 = 128 caractères (numérotés de 0 à 127).
- Il contient l’alphabet latin majuscule/minuscule non accentué (
A-Z,a-z), les chiffres (0-9), les signes de ponctuation usuels et des caractères de contrôle non imprimables (comme\npour le saut de ligne ou\tpour la tabulation).
| Caractère | Décimal | Binaire (7 bits) | Hexadécimal |
|---|---|---|---|
"A" |
65 | 100 0001 |
0x41 |
"B" |
66 | 100 0010 |
0x42 |
"a" |
97 | 110 0001 |
0x61 |
"0" |
48 | 011 0000 |
0x30 |
" " (espace) |
32 | 010 0000 |
0x20 |
⚠️ Limite majeure de l’ASCII :
L’ASCII est purement anglophone. Il ne comporte aucun accent (impossible d’écrire é, à, ç), ni caractères d’autres langues (grec, cyrillique, arabe, kanjis japonais…).
2. Les extensions sur 8 bits : l’ISO-8859-1 (Latin-1)
Puisque la mémoire physique est organisée en octets (8 bits), un bit restait inutilisé avec l’ASCII. La norme ISO-8859-1 (aussi appelée Latin-1) a utilisé ce 8e bit pour doubler le nombre de symboles possibles : 28 = 256 caractères (de 0 à 255).
- Les 128 premiers caractères (0 à 127) sont strictement identiques à l’ASCII.
- Les 128 caractères supplémentaires (128 à 255) contiennent les caractères accentués d’Europe occidentale (
é,à,ñ,ß…).
⚠️ Le problème de la fragmentation mondiale (le Mojibake) :
256 caractères ne suffisant pas pour l’ensemble des langues du monde, chaque région a développé sa propre variante (ISO-8859-5 pour le russe, ISO-8859-7 pour le grec, etc.). Si un fichier rédigé dans une variante était ouvert avec une autre, le texte devenait illisible (par exemple é à la place de é) : c’est le phénomène de mojibake.
3. La solution universelle : Unicode et UTF-8
Pour unifier toutes les langues au sein d’un format unique, le consortium Unicode a été créé en 1991.
A. Le répertoire Unicode
Unicode n’est pas un format de fichier, mais un catalogue universel qui associe un numéro unique (appelé point de code, noté U+XXXX) à chaque caractère ou symbole existant (plus de 155 000 symboles couvrant tous les alphabets du monde – y compris ceux de l’antiquité, symboles mathématiques et émojis) avec un maximum théorique de plus d’un million !
B. L’encodage UTF-8 (longueur variable de 1 à 4 octets)
UTF-8 (Universal Character Set Transformation Format - 8-bit) est la méthode la plus intelligente et la plus répandue pour stocker les points de code Unicode en mémoire :
- Taille variable : Chaque caractère est codé sur 1, 2, 3 ou 4 octets selon sa rareté.
- 100 % rétrocompatible avec l’ASCII : Les 128 caractères ASCII sont codés sur 1 seul octet (avec un premier bit à
0), à l’identique de l’ASCII historique. - Les caractères accentués courants (comme
é) prennent 2 octets. - Les écritures asiatiques prennent généralement 3 octets.
- Les émojis et symboles rares prennent 4 octets.
💡 Le standard incontesté du Web :
Aujourd’hui, plus de 98 % des sites internet et la quasi-totalité des systèmes d’exploitation modernes (sauf Windows 🤮) utilisent UTF-8 par défaut.
Manipulation des caractères en Python
Python fournit deux fonctions natives pour convertir un caractère en son point de code numérique et inversement :
| Fonction | Rôle | Exemple | Résultat |
|---|---|---|---|
ord(caractere) |
Renvoie le code numérique (ordinal) d’un caractère (1 chaîne de longueur 1) | ord("A")ord("é") |
65233 |
chr(code) |
Renvoie le caractère correspondant au code entier donné | chr(65)chr(8364) |
"A""€" |
# ord() et chr() sont des opérations inverses :
print(chr(ord("Z"))) # Affiche "Z"
# L’ordre alphabétique respecte l’ordre des codes :
print(ord("B") - ord("A")) # 66 - 65 = 1
# Parcourir les lettres de l’alphabet latin :
alphabet = [chr(i) for i in range(ord("A"), ord("Z") + 1)]
print("".join(alphabet)) # "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
Décodage d’un message en ASCII
On intercepte une suite d’octets représentée sous forme hexadécimale :
4E 53 49
1. À l’aide de l’extrait de table ci-dessous, retrouvez le message en clair.
0x4E= 78 en décimal0x53= 83 en décimal0x49= 73 en décimal- Code 65 =
"A", 73 ="I", 78 ="N", 83 ="S"
2. Quel est le code hexadécimal du caractère "!" sachant que ord("!") vaut 33 ?
Correction
1. Décodage :
0x4E(78) ⟶ N0x53(83) ⟶ S0x49(73) ⟶ I
Le message intercepté est « NSI ».
2. Code hexadécimal de "!" :
33 = 2 × 16 + 1 ⟶ 0x21 (ou (21)16).
Taille d’un texte en mémoire avec UTF-8
On rappelle les règles d’encodage UTF-8 :
- Caractères ASCII (code ≤ 127, non accentués) : 1 octet.
- Caractères accentués d’Europe (128 à 2047) : 2 octets.
- Émojis (code ≥ 65536) : 4 octets.
Calculez la taille en octets nécessaire pour encoder en UTF-8 les chaînes suivantes :
"Python""Numérique""NSI 🐍"
Correction
"Python": 6 caractères ASCII non accentués ⟶ 6 × 1 = 6 octets."Numérique": 8 caractères ASCII (N, u, m, r, i, q, u, e) + 1 caractère accentué (ésur 2 octets) ⟶ 8 + 2 = 10 octets."NSI 🐍": 3 lettres ASCII (3 octets) + 1 espace (1 octet) + 1 émoji (4 octets) ⟶ 3 + 1 + 4 = 8 octets.
Chiffrement de César en Python
Le chiffrement de César consiste à décaler chaque lettre majuscule d’un nombre fixe de rangs dans l’alphabet (clé $*k*$).
1. Que renvoie l’expression chr(ord("C") + 3) ?
2. La fonction suivante pour chiffrer une lettre majuscule passée en paramètre :
def decaler_majuscule(lettre, decalage):
# On ramène le code entre 0 et 25, on ajoute le décalage modulo 26, puis on réaligne sur "A"
code_relatif = ord(lettre) - ord("A")
nouveau_code = (code_relatif + decalage) % 26
return chr(ord("A") + nouveau_code)
On rappelle que A%B renvoie le reste de la division euclidienne de A par B. La division eculidienne, c’est celle que vous avez apprise au primaire : quotien et reste.
Prévoir ce que vont renvoyer les appels suivants, en déterminant les valeur calculées à chaque étape intermédiaire (lignes 3 et 4).
decaler_majuscule("A", 3)decaler_majuscule("Z", 1)
On donne : ord("A") = 65, ord("B") = 66, … , ord("Z") = 90.
3. Écrire la fonction coder_mot(mot, decalage) qui transforme un mot complet en le décalant avec le décalage entré.
Correction
1. ord("C") vaut 67.
67 + 3 = 70. chr(70) renvoie "F".
2.a. decaler_majuscule("A", 3) :
code_relatif = 65 – 65 = 0
nouveau_code = (0 + 3) % 26 = 3 – le reste de 3÷26, vaut 3.
ord("A") + nouveau_code = 65 + 3 = 68.
chr(68) correspond à "D"
2.b. decaler_majuscule("Z", 1) :
code_relatif = 90 – 65 = 25
nouveau_code = (25 + 1) % 26 = 0 – le reste de 26÷26, vaut 0.
ord("A") + nouveau_code = 65 + 0 = 65.
chr(65) correspond à "A"
3. Fonction coder_mot.
def coder_mot(mot, decalage):
code = ""
for lettre in mot:
code += decaler_majuscule(lettre, decalage)
return code