Types et valeurs de base

Les élèves doivent savoir
  • Passer de la représentation d’une base dans une autre.
  • É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.
  • Utiliser le complément à 2.
  • Calculer sur quelques exemples la représentation de nombres réels : 0.1, 0.25 ou 1/3.
  • Dresser la table d’une expression booléenne.
  • Identifier l’intérêt des différents systèmes d’encodage.
  • Convertir un fichier texte dans différents formats d’encodage.

10.    Écriture d’un entier positif

Nombres et puissances de dix

En sciences et dans la vie quotidienne vous avez pris l’habitude de travailler dans la base dix, correspondant aux dix doigts des mains et où les chiffres vont de 0 à 9 (ce qui fait bien dix chiffres : 0, 1, 2, 3, 4, 5, 6, 7, 8, 9).

Les nombres sont des assemblages de chiffres pour pouvoir représenter des valeurs supérieures à 9. Au-delà du 9 (que l’on peut imaginer comme précédé du zéro : 09), un « 1 » sera rajouté devant le nombre et le chiffre suivant reprend à 0. Arrivé à 19, on remplace le « 1 » qui était devant le nombre par « 2 » et ainsi de suite jusqu’à 99 dont le nombre suivant (100) nécessitera l’ajout d’un chiffre supplémentaire et une reprise des deux derniers chiffres à « 00 », donnant ainsi « 100 ».

L’écriture scientifique est une représentation des nombres où l’on ne garde qu’un seul chiffre avant la virgule, puis un certain nombre de chiffres significatifs après celle-ci, l’ensemble étant multiplié par une puissance de dix :

135 aura l’écriture scientifique  indiquant que 1,35 doit être multiplié 2 fois par 10 : 1,35 \times 10 \times 10=135

Il est à noter qu’un simple chiffre pourrait être noté, en écriture scientifique, comme multiplié par 10 puissance 0, donc 0 fois par 10 ! Exemple : 1,5=1,5 \times 10^{0}

Un nombre à plusieurs chiffres peut aussi être découpé en fonction du « rang » de ces chiffres : unité, dizaine, centaine, millier…. Et associé à la puissance de dix correspondante.

On pourrait écrire que 135=1 \times 10^{2}+3 \times 10^{1}+5 \times 10^{0}

Le rang correspondant à la puissance de dix la plus élevée est appelé « rang de poids fort » et celui associé à la plus petite puissance de dix est le « rang de poids faible ».

Dans « 135 », le rang de poids fort vaut 1 et le rang de poids faible vaut 5.

Autre exemple : 2020=2 \times 10^{3}+0 \times 10^{2}+2 \times 10^{1}+0 \times 10^{0}

Binaire : base 2

Hérité des premiers systèmes de programmation mécanique, à base de trous ou de pleins (cartes perforées des métiers à tisser Jacquard, orgues de barbarie, pianos mécaniques…), puis à base de systèmes électriques à deux états (courant/pas de courant), le système de numération adopté dans la programmation et le fonctionnement interne des ordinateurs est logiquement en base 2, dit « binaire ».

Dans cette base de calcul, seuls deux chiffres sont possibles : 0 ou 1

En binaire on va compter 0, puis 1, puis il faut passer à deux chiffres en ajoutant 1 devant, donc 10 puis 11. A ce stade il faut déjà passer à 3 chiffres en ajoutant 1 devant et en recommençant à 0 pour les chiffres suivants : 100, 101, 110, 111 avant de devoir ajouter un chiffre supplémentaire.

Les nombres binaires sont donc une suite de 0 et de 1, suivant le même principe de numération que les nombres décimaux, sauf que cette fois-ci la décomposition se fait sur des puissances de 2 au lieu des puissances de 10.

Le nombre binaire 11010 vaut donc :

(11010)_{2}=1 \times 2^{4}+1 \times 2^{3}+0 \times 2^{2}+1 \times 2^{1}+0 \times 2^{0}=16+8+0+2+0=(26)_{10}

Le chiffre en indice du résultat indique que ce résultat est en base 10, alors que celui en bas du nombre binaire indique que l’on est en base 2.

Le système de rang est similaire à celui de la notation décimale : le rang le plus élevé est le rang de poids fort et le rang zéro est le rang de poids faible.

Autre exemple : (1010)_{2}=1 \times 2^{3}+0 \times 2^{2}+1 \times 2^{1}+0 \times 2^{0}=8+0+2+0=(10)_{10}

Chaque chiffre binaire est nommé « BIT » pour BInary digiT et ceux-ci sont regroupés par 8 pour former un « octet ».

Hexadécimal : base 16

Lorsque l’on regroupe 4 bits ensemble en binaire, cela donne :

1 \times 2^{3}+1 \times 2^{2}+1 \times 2^{1}+1 \times 2^{0}=8+4+2+1=(15)_{10}

Pour un nombre binaire sur 4 bits, il y a donc 16 nombres possibles en base décimale.

Comme les nombres binaires sont regroupés en octet, soit deux fois quatre bits, les informaticiens utilisent la base de numération hexadécimale pour résumer un octet de 8 bits à un nombre hexadécimal de deux chiffres.

En base 16, les chiffres possibles sont : 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F. Les lettres supplémentaires utilisées correspondent, en décimal, aux nombres suivants : A=10, B=11, C=12, D=13, E=14 et F=15.

(A 1 F B)_{16}=10 \times 16^{3}+1 \times 16^{2}+15 \times 16^{2}+11 \times 16^{0}=(41467)_{10}

Nous avons déjà vu, lors du chapitre sur le web, que les couleurs sont codées sous forme hexadécimale, généralement sous la forme de 3 nombres à deux chiffres qui indiquent l’intensité du rouge, du vert et du bleu. Cela est dû au fait que les standards d’affichage des couleurs ont longtemps été basés sur des affichages où il n’était possible de coder une couleur que sur un octet et donc de ne lui donner que 256 valeurs (16×16=256). Ces couleurs sont ensuite assemblées par synthèse additive, où l’absence de couleur correspond à (00)16=(0)10 et l’intensité maximale à (FF)16=(255)10.

Toutes les couleurs éteintes correspondent donc au noir (code (000000)16) et toutes les couleurs allumées correspondent au blanc (code (FFFFFF)16). Dans ce système de codage, il y a donc 256(R) \times 256(V) \times 256(B) \approx\left(16,7 \times 10^{6}\right)_{10} couleurs possibles (16,7 millions).

Notez que les écrans modernes dits « HDR » (High Dynamic Range), codent les couleurs en 10 ou 12 bits, au lieu des 8 bits précédents et permettent ainsi d’afficher une palette de couleur plus large et proche de la réalité. L’objectif étant, dans quelques années, de passer à des affichages où chaque couleur primaire (rouge, vert et bleu) sera codée sur 16 bits (deux octets), lorsque les puissances de traitement des puces embarquées dans les téléviseurs, appareils photo, caméscopes… seront suffisantes pour traiter cette forte augmentation des données.

Une image à 8 mégapixels (8 millions de points, équivalent de la « 4k »), soit avec des couleurs en 8 bits nécessite le traitement d’environ 4,2 milliards de bits, alors que la même image codée en couleurs 16 bits nécessitera le traitement de 34 milliards de bits. Ceci à multiplier par le nombre d’images par seconde (24 pour du cinéma, mais de plus en plus souvent 60 pour la vidéo).

Passer d’une base à une autre

Pour savoir dans quelle base les nombres sont écrits, nous allons les mettre entre parenthèses et indiquer la valeur de base en indice, de la façon suivante : (135)10 indique 135 en base décimale.

  • Passage vers la base 10

Comme nous l’avons vu dans les nombreux exemples précédents, le passage d’une base binaire ou hexadécimale au décimal est sans doute l’opération la plus simple puisqu’il suffit de multiplier les nombres de chaque rang par la valeur de la base élevée à la puissance du rang et d’additionner tous les termes.

Exemple : (F15A30)16 à convertir en décimal. La base est hexadécimale, donc sa valeur est 16. Il y a 6 chiffres, donc les rangs iront de 5 à 0 (de gauche à droite). En oubliant pas que F vaut 15 et A vaut 10 en base décimale.

(\mathrm{F} 15 \mathrm{A} 30)_{16}=15 \times 16^{5}+1 \times 16^{4}+5 \times 16^{3}+10 \times 16^{2}+3 \times 16^{1}+0 \times 16^{0}

 

(\mathrm{F} 15 \mathrm{A} 30)_{16}=15728640+65536+20480+2560+48+0=(15817264)_{10}
  • Passage du décimal au binaire ou à l’hexadécimal

Lors du passage d’une base décimale à une autre base, il suffit de diviser le nombre à convertir par la valeur de la base en une série d’opérations successives tant que le quotient de la division n’est pas nul. On prendra alors les résultats entiers de chaque division dans l’ordre inverse pour obtenir le résultat souhaité.

Voici ce que cela donne avec quelques exemples :

Donc (2020)10=(11111100100)2

Donc (2020)10=(7E4)16

  • Passage du binaire à l’hexadécimal

Pour passer du binaire à l’hexadécimal, il faut se souvenir que 4 bits représentent 16 possibilités de valeur et correspondent donc à un chiffre hexadécimal.

On divise donc le nombre binaire en séquences de 4 bits qui sont converties en une suite de chiffres hexadécimaux qu’il suffit de rassembler dans l’ordre. Le découpage en séquence de 4 bits se fait de droite à gauche et on rajoute des 0 devant la séquence de gauche si nécessaire

Voici un exemple :

  • Passage de l’hexadécimal au binaire

Pour passer de l’hexadécimal au binaire, on fait l’opération inverse : chaque chiffre hexadécimal est converti en une séquence de 4 bits et ces séquences sont ensuite assemblées dans l’ordre. Si l’une des séquences ne fait pas 4 bits, on complète à 4 bits en ajoutant des 0 devant le nombre obtenu :

Pour ces deux conversions, on utilise donc abondamment les conversions suivantes :

Décimal Hexadécimal Binaire Décimal Hexadécimal Binaire
0 0 0000 8 8 1000
1 1 0001 9 9 1001
2 2 0010 10 A 1010
3 3 0011 11 B 1011
4 4 0100 12 C 1100
5 5 0101 13 D 1101
6 6 0110 14 E 1110
7 7 0111 15 F 1111

11.    Représentation binaire d’un entier relatif

Représenter le signe d’un entier en binaire

Jusqu’ici nous n’avons vu que les nombres entiers positifs, Comme le binaire n’autorisent que deux symboles, « 0 » ou « 1 », il fallait trouver un moyen de représenter le signe des entiers relatifs (positifs et négatifs).

La solution la plus évidente a été utilisée pour les premiers ordinateurs : utiliser le bit de poids fort pour le signe avec la convention suivante :

  • Valeur « 1» pour les entiers négatifs
  • Valeur « 0» pour les entiers positifs

Le premier bit représente donc le signe et les bits suivants représentent la valeur absolue du nombre.

Ainsi, le nombre binaire relatif codé sur un octet suivant (le rang est en dessous) :

(10010100)=-\left(2^{4}+2^{2}\right)=-20

(76543210)

On voit ici que le rang de poids fort (7) est utilisé pour le signe et vaut « 1 » donc le nombre est négatif.

Une fois le signe établi, on repère les « 1 » dans le reste du nombre et on additionne les 2 puissances « le rang du 1 » pour savoir de quel nombre il s’agit.

Cette méthode de codage a un inconvénient majeur : deux nombres représentent le zéro (sur 4bit par exemple : (1000)2=(0000)2=(0)10). Pour traiter cela, il faut des circuits différents pour l’addition et la soustraction et cette méthode coûteuse en ressource a été remplacée par une méthode plus efficace : le complément à deux.

Complément à deux

Pour le complément à deux, on continue à regarder le rang de poids fort :

  • Valeur « 0» pour les entiers positifs : on prend les bits suivants pour déterminer la valeur du nombre.
  • Valeur « 1» pour les entiers négatifs :
    • On commence par représenter en binaire la valeur absolue du nombre.
    • On inverse ensuite tous les bits de ce nombre, cette opération est appelée « complément à 1 ».
    • Enfin on ajoute (par addition binaire, voir plus loin) « 1 » au nombre ainsi obtenu.

Pour connaître la valeur d’un nombre binaire entier négatif (qui commence donc par « 1 ») on procède exactement de la même façon dans le même ordre : inversion des bits puis ajout (addition binaire) de « 1 » pour connaître la valeur absolue positive :

Avec cette méthode de codage, une seule valeur représente 0 : sur 4 bits par exemple ce sera (0000)2=(0)10

Voici les valeurs sur 3 bits en complément à deux :

Binaire décimal binaire décimal
000 0 100 -4
001 1 101 -3
010 2 110 -2
011 3 111 -1

La limite de cette méthode de codage des nombres relatifs réside dans un risque d’erreurs de calcul si le codage se fait sur un nombre trop restreint de bits.

Par exemple, sur 3 bits, si on effectue l’opération (3+1)10=(011+1=100)2=(-4)10 ! De même, l’opération (-4-1)10=(100+111=(1)011)2=(3)10 ! Sur 4 bits, ces deux opérations ne présentaient pas d’erreur de calcul.

Dans les deux cas, on a dépassé les limites du codage possible sur 3bit, on parle alors de « débordement ». Pour éviter ce phénomène qui peut entraîner de graves erreurs de calcul, on a intérêt à coder les entiers sur un nombre de bits plus grand que la plus grande valeur qu’on va utiliser de façon que le bit de poids fort reste réservé au seul signe.

Vous pouvez trouver un convertisseur en ligne pour vérifier vos calculs sur ce site : https://sebastienguillon.com/test/javascript/convertisseur.html

12.    Représentation approximative des nombres réels

Nombres réels en base 10

Nous avons vu que l’écriture d’un nombre entier en base 10 peut se faire avec l’utilisation des puissances de 10.

Ainsi, le nombre (747)_{10}=7 \times 10^{2}+4 \times 10^{1}+7 \times 10^{0}

Comment faire avec les nombres à virgule, que l’on nomme nombres réels en mathématique ?

En sciences vous avez déjà vu que l’on pouvait utiliser des puissances de dix négatives pour représenter les chiffres qui se trouvent à droite de la virgule d’un nombre réel :

0,1=10^{-1}=\frac{1}{10^{1}} \text { ou } 0,01=10^{-2}=\frac{1}{10^{2}}

Un nombre réel peut se décomposer de la façon suivante : une partie entière et une partie fractionnaire. La partie entière représente les nombres situés à gauche de la virgule et la partie fractionnaire est la partie à droite de la virgule. On pourra décomposer un nombre réel de la façon suivante :

(747,128)_{10}=7 \times 10^{2}+4 \times 10^{1}+7 \times 10^{0}+1 \times 10^{-1}+2 \times 10^{-2}+8 \times 10^{-3}

Remarquez que pour les chiffres situés après la virgule (à droite de la virgule), la puissance de dix correspond à la position précédée d’un signe moins : « -1 » est le premier chiffre après la virgule, « -2 » le second… et « -8 » serait le huitième chiffre après la virgule.

Nombres réels en binaire

En binaire il n’y a que deux valeurs possibles, 0 et 1. Mais il est également possible d’y écrire un nombre réel en reprenant le même type d’écriture que pour la base 10 : écrire les chiffres avant la virgule, puis la virgule, puis les chiffres après la virgule. Par exemple « 1011,1101 » est un nombre réel binaire.

Passage du binaire à la base 10

Ce nombre binaire peut être converti en nombre décimal en le décomposant en puissance de deux. Les chiffres à droite de la virgule correspondent à des puissances de deux négatives :

(1011,1101)_{2}=1 \times 2^{3}+0 \times 2^{2}+1 \times 2^{1}+1 \times 2^{0}+1 \times 2^{-1}+1 \times 2^{-2}+0 \times 2^{-3}+1 \times 2^{-4}

Que l’on peut également écrire de la façon suivante :

(1011,1101)_{2}=1 \times 2^{3}+0 \times 2^{2}+1 \times 2^{1}+1 \times 2^{0}+1 \times \frac{1}{2^{1}}+1 \times \frac{1}{2^{2}}+0 \times \frac{1}{2^{3}}+1 \times \frac{1}{2^{4}}

Il est alors possible de résoudre ce calcul pour retrouver la valeur de ce nombre en base dix :

(1011,1101)_{2}=8+0+2+1+0,5+0,25+0+0,0625=11,8125

Passage de la base 10 au binaire

Pour passer d’un nombre réel en base dix à son écriture binaire, il faut commencer par le décomposer en deux parties : la partie entière et la partie fractionnaire. Chaque partie sera convertie séparément.

Si nous reprenons le nombre « 11,8125 », nous commençons par convertir (11)10 en binaire par des divisions entières successives par deux, comme vu précédemment :

Division Résultat entier reste
11÷2 5 1
5÷2 2 1
2÷2 1 0
1÷2 0 1

On lit le résultat des restes de bas en haut et on obtient : (11)10=(1011)2

Pour la partie fractionnaire, « 0,8125 » nous allons effectuer des multiplications par deux successives, en conservant les nombres entiers à gauche de la virgule et en continuant à multiplier les nombres obtenus à droite de la virgule jusqu’à obtenir un résultat de zéro.

Multiplication On garde Reste à traiter
0,8125×2=1,625 1 0,625
0,625×2=1,25 1 0,25
0,25×2=0,5 0 0,5
0,5×2=1 1 0

On lit cette fois les valeurs entières conservées de haut en bas : « 1101 ». donc (0,8125)10=(0,1101)2

Il reste à additionner les deux parties pour retrouver notre conversion décimale vers binaire complète : (11,8125)10=(1011,1101)2

Limites de précision dans un ordinateur : nombres flottants

Maintenant que nous sommes capables de convertir des nombres réels de binaire vers la base dix et inversement, il est important de réaliser les limites de cette conversion :

  • Que faire si nous voulons convertir un nombre réel qui possède une infinité de chiffres après la virgule, comme 1/3 ou 1/6, sans oublier les grandes constantes comme Pi ?
  • Que faire si la conversion décimale vers binaire ne se « termine jamais », comme pour la conversion (0,1)10 en binaire (essayez) ?
  • Comment traiter les nombres qui ont beaucoup de zéros après la virgule, comme « 0,000000244 » ?
  • Enfin la question principale : comment coder un nombre dans un ordinateur quand on ne sait pas par avance où va se trouver la virgule et donc combien de chiffres il faut prévoir avant ou après celle-ci ?

Les deux premières questions ont une réponse simple : si le nombre réel ne peut être écrit dans l’espace mémoire qui lui est alloué, il est nécessaire d’en faire une approximation. Mais comme cette approximation est nécessairement une valeur qui est simplement approchée et pas « exacte », il est très important de ne pas utiliser des nombres flottants pour des comparaisons (nous y revenons plus bas).

Pour représenter un nombre réel quelconque sans se poser de questions sur la position de la virgule, nous allons utiliser l’écriture scientifique : on garde un chiffre avant la virgule, d’autres chiffres après et on multiplie le tout par la puissance de dix correspondante.

Par exemple : 1747 sera écrit, en écriture scientifique, 1,747.103 (où le point représente une multiplication). 0,0000002 sera écrit 2.10-7. Les zéros « inutiles » sont ainsi ‘éliminés’ et l’écriture du nombre prend moins de place.

En informatique, les nombres réels seront représentés en « virgule flottante » d’une façon similaire à l’écriture scientifique. La norme la plus courante pour cette représentation est la norme IEEE 754 (IEEE pour Institute of Electrical and Electronics Enineers) qui propose deux formats de nombres à virgule flottante :

  • Float : dit « simple précision » avec un codage sur 32 bits
  • Double : dit « double précision » avec un codage sur 64 bits

Dans les deux cas, le nombre est décomposé de la façon suivante :

(signe)Mantisse×2Exposant

Nous voyons trois parties dans cette représentation :

  • Signe : codé sur 1bit, il vaudra « 0 » pour un nombre positif et « 1 » pour un nombre négatif.
  • Mantisse : il s’agit de notre nombre écrit en écriture scientifique. Comme il est en binaire, pour économiser un bit (et ajouter de la précision), il commence toujours par « 1 ». Le codage de la mantisse se fait sur 23 bits en float et sur 52 bits en double.
  • Exposant : c’est la puissance de deux. Comme il n’a pas été prévu de signe pour l’exposant, on utilise une méthode appelée « biais » qui consiste à décaler ce nombre de (127)10 ce qui donne des puissances comprises entre (-126)10 et (+127)10 en float. Le décalage sera de 1023 en double.

En mémoire le stockage se fera de la façon suivante : (chaque carré représente 1bit : une case en mémoire)

Conversion de base 10 en nombre flottant

Pour passer d’un nombre réel en base 10 à un nombre flottant, il faut effectuer les opérations suivantes :

  • Déterminer le signe pour trouver la valeur du premier bit
  • Convertir le nombre en binaire, enlever le premier « 1 » et noter le nombre binaire obtenu dans la mantisse en calant ce nombre à gauche et en complétant par des zéros à droite si nécessaire.
  • Noter le décalage qui a été nécessaire pour la virgule (afin d’écrire le nombre binaire sous la forme 1,xxxxx) et lui ajouter 127. Convertir le résultat en binaire, l’écrire dans l’exposant en le calant à droite et compléter par des zéros à gauche si nécessaire.

Prenons deux exemples :

  • Conversion de (12,125)10 en float

Ici le signe est positif, notre float va donc commencer par « 0 »

Nous devons maintenant convertir (12,125)10 en binaire :

Nombre à diviser Donne Reste
12÷2 6 0
6÷2 3 0
3÷2 1 1
1÷2 0 1

Lecture de bas en haut : (12)10=(1100)2

Passons à la partie fractionnaire :

Nombre à multiplier On garde Reste à traiter
0,125×2=0,250 0 0,250
0,250×2=0,500 0 0,500
0,500×2=1 1 0

On lit de haut en bas : (0,125)10=(0,001)2

On rassemble les deux : (12,125)10=(1100,001)2

Pour passer à une écriture en float il faut décaler la virgule afin de ne garder que le premier « 1 » à gauche de celle-ci :

(1100,001)2 = 1,100001×23 = (1,100001×211)2

La puissance de 2 a été convertie en binaire : (3)10=(11)2

Pour écrire la mantisse, on enlève le 1 qui est à gauche de la virgule et on obtient « 100001 ». Il faut compléter ce nombre avec des zéros à droite pour arriver à un total de 23 bits en float : « 10000100000000000000000 ».

L’exposant vaut « 11 » en binaire et « 3 » en base dix. Il faut lui ajouter « 127 » (base dix) ce qui fera : 127+3=130. On converti ensuite « 130 » en binaire :

Nombre à diviser Donne Reste
130÷2 65 0
65÷2 32 1
32÷2 16 0
16÷2 8 0
8÷2 4 0
4÷2 2 0
2÷2 1 0
1÷2 0 1

On lit de bas en haut et (130)10 = (10000010)2

Cet exposant fait déjà 8 bits, il n’est donc pas nécessaire d’ajouter des zéros à gauche. L’exposant vaut donc « 10000010 ».

On rassemble les trois morceaux pour obtenir le résultat de la conversion :

(12,125)10 = (0 10000010 10000100000000000000000)2­_float

Comme ce nombre est un peu long à écrire en binaire, on peut aussi l’écrire en hexadécimal : (41420000)16

  • Conversion de (-425,1)10 en float

Ici le signe est négatif, donc le bit de signe vaut « 1 »

Conversion de (425)10 :

Nombre à diviser Donne Reste
425÷2 212 1
212÷2 106 0
106÷2 53 0
53÷2 26 1
26÷2 13 0
13÷2 6 1
6÷2 3 0
3÷2 1 1
1÷2 0 1

On lit de bas en haut : (425)10 = (110101001)2

Conversion de (0,1)10 :

Nombre à multiplier On garde Reste à traiter
0,1×2=0,2 0 0,2
0,2×2=0,4 0 0,4
0,4×2=0,8 0 0,8
0,8×2=1,6 1 0,6
0,6×2=1,2 1 0,2
0,2×2=0,4 0 0,4
0,4×2=0,8 0 0,8
0,8×2=1,6 1 0,6
0,6×2=1,2 1 0,2
0,2×2=0,4 0 0,4
0,4×2=0,8 0 0,8
0,8×2=1,6 1 0,6
0,6×2=1,2 1 0,2
0,2×2=0,4 0 0,4
0,4×2=0,8 0 0,8
0,8×2=1,6 1 0,6
0,6×2=1,2 1 0,2
0,2×2=0,4 0 0,4
0,4×2=0,8 0 0,8

On est ici face à un souci : le calcul ne se termine pas !

(0,1)10=(0,000110011001100110011…)2

La seule solution sera de tronquer ce nombre en fonction du nombre de bits disponibles.

On rassemble nos deux parties :

(425,1)10=(110101001,000110011001100110011…)2

Il faut maintenant décaler la virgule pour une écriture sous forme scientifique. Comme nous ne gardons que 23 bits pour la mantisse et que la partie à gauche de la virgule contient déjà 9 chiffres, il faut arrondir la partie fractionnaire. Si on enlève le premier chiffre qui sera avant la virgule, le 23e chiffre après la virgule est un zéro et le 24e est un « 1 ». L’arrondi du « 0 » suivi de « 1 » conduit à transformer le « 0 » en « 1 », de la même façon qu’en base 10, on arrondit toujours au supérieur à partir de « 0,5 » :

(110101001,00011011001100110011…)2 = 1,10101001000110011001101×28

La mantisse vaut donc (on enlève le 1 avant la virgule) : « 10101001000110011001101… »

Pour trouver l’exposant on effectue le calcul : 127+8=(135)10=(10000111)2

L’exposant vaut donc « 10000111 »

En rassemblant tous les éléments :

(425,1)10 = (1 10000111 10101001000110011001101)2­_float

Ce nombre pourra également s’écrire en hexadécimal : 43D48CCD

Conversion de nombre flottant en base 10

La conversion d’un nombre flottant en nombre en base dix consiste à faire exactement l’inverse de l’opération précédente :

  • On identifie le signe avec le premier bit
  • On prend les 8 bits suivants (pour un float), on les convertit en base 10 et on leur soustrait 127 pour avoir l’exposant de 2 indiquant le décalage de la virgule.
  • On rajoute 1 devant la mantisse (les 23 bits de la fin du float) et on décale la virgule.
  • On convertit les deux parties du nombre obtenu (avant la virgule et après la virgule) en base dix comme vu précédemment.

Voici un exemple : conversion de (1 10000111 10101001000110011001101)2­_float en base 10 :

Le bit de signe vaut « 1 » donc c’est un nombre négatif.

L’exposant vaut « 10000111 » que l’on convertit en « 135 ». On effectue la soustraction de 127 : 135-127=8. Il faudra donc décaler la virgule de 8.

On reprend la mantisse en ajoutant « 1 » devant, ce qui donne « 1,10101001000110011001101 ». On décale la virgule de 8 vers la droite et on obtient :

110101001,000110011001101

On convertit les deux parties du nombre :

Pour la partie à gauche de la virgule : (110101001)2=(425)10

Pour la partie à droite de la virgule on effectue le calcul suivant :

0×2-1+0×2-2+0×2-3+1×2-4+1×2-5+0×2-6+0×2-7+1×2-8+1×2-9+0×2-10+0×2-11+1×2-12+1×2-13+0×2-14+1×2-15=2-4+2-5+2-8+2-9+2-12+2-13+2-15≃0,100006

Donc (1 10000111 10101001000110011001101)2­_float≃(-425,100006)

Surprise : on constate que la conversion de la base dix vers un nombre flottant et son opération inverse ne nous redonne pas le nombre de départ !

Cela est dû à l’arrondi qui a été fait au moment de la conversion de la partie fractionnaire en binaire.

Il est donc fondamental de ne JAMAIS effectuer des comparaisons entre nombres fractionnaires en programmation, car pour un ordinateur qui travaille avec des nombres flottants : 0,1+0,2 ≠0,3 !

Vous pouvez utiliser le site suivant pour vérifier vos calculs :
https://www.h-schmidt.net/FloatConverter/IEEE754.html

13.    Valeurs et expressions booléennes

Valeurs booléennes et opérateurs booléens

Comme nous l’avions vu au début de ce cours de NSI, la notion de chiffres qui ne peuvent prendre que deux valeurs date de l’antiquité. C’est toutefois le mathématicien anglais George Bool qui en définit les bases mathématiques au milieu du XIXe siècle. Cette branche des mathématiques est aussi référencée comme « logique mathématique ».

Un « booléen », comme on nomme désormais ces chiffres, ne peut prendre que deux valeurs :

  • 0 (zéro) qui peut aussi être remplacé par le mot « False » (faux)
  • 1 (un) qui peut aussi être remplacé par le mot « True » (vrai)

Ces valeurs sont très pratiques lorsque l’on confie des calculs à des ordinateurs, car dans les circuits électriques de ceux-ci, le courant peut circuler ou ne pas circuler, ce qui correspond alors aux deux états booléens :

À partir de ces deux états, il est possible de faire des opérations avec des opérateurs qui peuvent s’appliquer à la fois à leur valeur (0 ou 1) ou à leur représentation logique (Vrai ou Faux).

Les principaux opérateurs booléens utilisés sont :

  • AND (et) : produit logique, aussi noté : A⋅B
  • OR (ou inclusif) : addition logique, aussi notée : A+B
  • NOT (non) : inversion, aussi notée :
  • XOR (ou exclusif) : aussi noté : A⊕B

Tables de vérité

A chacune de ces opérations correspond une « table de vérité » qui indique l’état après une (ou plusieurs) opération logique. On peut également représenter les opérations de base par leur équivalent en circuit électrique. Ces circuits de bases sont présents dans les circuits électroniques des ordinateurs sous la forme de composants qui intègrent l’ensemble de l’opération (tous les « interrupteurs » représentés ci-dessous) dans ce que l’on appelle des « portes logiques »

  • AND

Le produit logique AND peut être représenté par un tableau, appelé « table de vérité » comme celui-ci (avec 0 et 1 que l’on pourrait aussi représenter par les équivalents logiques False et True) :

Première valeur (A) Seconde valeur (B) A AND B
0 (False) 0 (False) 0 (False)
0 (False) 1 (True) 0 (False)
1 (True) 0 (False) 0 (False)
1 (True) 1 (True) 1 (True)
  • OR

OR, également appelé ‘ou inclusif’ qui correspond à l’addition logique (attention, pas à l’addition booléenne que nous verrons plus loin) :

Table de vérité :

Première valeur (A) Seconde valeur (B) A OR B
0 (False) 0 (False) 0 (False)
0 (False) 1 (True) 1 (True)
1 (True) 0 (False) 1 (True)
1 (True) 1 (True) 1 (True)

 

  • NOT

NOT correspond à l’inversion booléenne : Vrai devient Faux et Faux devient Vrai :

Table de vérité :

Première valeur (A) NOT A
0 (False) 1 (True)
1 (True) 0 (False)

 

  • XOR

Pour XOR nous devons faire appel à des interrupteurs différents, comme ceux de NOT, combinée à des interrupteurs classiques. Cela semble peut-être un peu compliqué et marque la limite de la représentation « simple » des opérations logiques par des circuits électriques.

Table de vérité :

Première valeur (A) Seconde valeur (B) A XOR B
0 (False) 0 (False) 0 (False)
0 (False) 1 (True) 1 (True)
1 (True) 0 (False) 1 (True)
1 (True) 1 (True) 0 (False)

Expression booléenne

Les opérations booléennes peuvent être combinées entre elles pour donner des expressions booléennes. Le résultat de ces opérations peut alors être rassemblé dans un tableau.

Exemple : cherchons le résultat de l’opération « (NOT A) AND (NOT B) »

A B NOT A NOT B (NOT A) AND (NOT B)
0 0 1 1 1
0 1 1 0 0
1 0 0 1 0
1 1 0 0 0

Vous voyez que pour trouver le résultat final nous avons dû chercher d’abord le résultat du calcul des valeurs entre parenthèses.

Si nous compliquons encore notre tableau en évaluant l’expression « NOT((NOT A) AND (NOT B)) » :

A B NOT A NOT B (NOT A) AND (NOT B) NOT((NOT A) AND (NOT B))
0 0 1 1 1 0
0 1 1 0 0 1
1 0 0 1 0 1
1 1 0 0 0 1

Nous remarquons que le résultat est le même que celle du « ou inclusif » « OR ».

Si les tableaux de vérités de deux expressions (ou opérateurs) booléens sont les mêmes, on dit que ces expressions sont équivalentes. C’est la base de l’algèbre de Bool.

Ici nous avons donc : NOT((NOT A) AND (NOT B)) = A OR B

Ce qui sera écrit, avec le formalisme de l’algèbre de bool :

Caractère séquentiel des opérations booléennes

Ces expressions sont essentielles en programmation puisque c’est avec elles qu’il est possible de faire des choix avec les branchements par conditions « if…elif…else » ou des boucles « while ».

Lors de la compilation ou de l’interprétation du code, les expressions booléennes seront interprétées dans l’ordre de leur écriture. Il est donc important, comme dans le reste des mathématiques, de bien mettre des parenthèses pour les parties d’expressions que l’on évaluer ensemble.

Certains langages, comme le C++ ou le Python, accélèrent l’exécution du code en ne faisant que l’évaluation du premier argument quand le résultat d’une opération dépend de lui :

  • OR : si le premier argument est vrai, le second ne sera pas évalué
  • AND : si le premier argument est faux, le second ne sera pas évalué

Il est important de tenir compte de cette façon d’évaluer les expressions dans ces langages.

Voici la syntaxe des opérations logiques en Python où il faudra écrire « True » et « False » avec une majuscule en début de mot :

  • AND : « a and b »
  • OR : « a or b »
  • NOT : « not a »
  • XOR : « a^b »

Addition binaire

L’addition binaire fonctionne de la même manière qu’une addition en base dix, mais la retenue va se faire plus rapidement.

1 + 0 = 1

1 + 1 = 10 : on pose zéro (0) et on retient un (1) qui va se mettre devant le zéro.

Pour l’addition binaire de nombres binaires comportant plusieurs chiffres, il faudra faire « suivre » les retenues. Il est souvent plus simple de ‘poser’ ces additions en mettant les nombres l’un sur l’autre :

Ainsi on voit que (11)2+(11)2=(110)2

Ce calcul nous montre également qu’il faut faire particulièrement attention aux dépassements de capacités mémoire lors des calculs en binaire. En effet, nous sommes partis de nombres sur 2 bits, qui ont donc des valeurs décimales comprises entre 0 et 3, et nous avons additionné (3)10+(3)10=(6)10. Or 6 ne peut pas être écrit avec deux bits, mais en nécessite 3.

Si on additionne des nombres binaires sur 8 bits et que le résultat est également conservé en mémoire sur 8 bits, il est tout à fait possible que le résultat soit tronqué (et donc faux), dès lors que celui-ci dépasse la valeur (255)10=(11111111)2. En effet, il faut 9 bits pour coder (256)10 et plus en binaire.

On peut également faire des soustractions (addition d’un nombre négatif en complément à deux) ou des multiplications en binaire avec les mêmes méthodes que pour un calcul en base 10. Pour la multiplication il faudra toutefois s’assurer de disposer d’un nombre de bits suffisant pour afficher le résultat.

14.    Représentation d’un texte en machine

Coder un texte sur ordinateur

La communication de textes à distance à l’aide d’un signal électrique date du XIXe siècle avec le télégraphe (à partir de 1840) qui utilisait le système de codage de caractères de Morse (inventé en 1837) : chaque caractère était codé par une série de points et de traits (signal long ou signal court).

En 1963, l’association américaine responsable des standards de certifications pour l’industrie formalise un codage inspiré du télégraphe pour l’échange de texte par voie informatique : the American Standard Code for Information Interchange, ASCII. Ce codage reprend initialement les lettres, uniquement majuscules, du télégraphe et y ajoute, en 1967, les lettres minuscules en plus d’un certain nombre de codes pour le contrôle du texte (espace, passage à la ligne…).

A chaque caractère est associé un nombre binaire codé sur un nombre défini de bit. Pour éviter des erreurs de transcodage (conversion inverse du binaire vers du texte à la lecture du code), on ajoute un bit de contrôle, appelé « bit de parité », dont la valeur est fixe. Si ce bit est modifié, on sait que le code a été altéré par un bug ou une erreur de transmission (corruption d’un paquet TCP/IP par une interférence électrique par exemple).

Le codage d’un texte sur ordinateur consiste donc à transformer chaque lettre en son code binaire afin de pouvoir effectuer le stockage et le traitement dans un ordinateur. Afin de simplifier l’écriture, on peut ensuite convertir des paquets de 8 bits (octets) en écriture hexadécimale, comme vu au début de ce chapitre.

Norme ASCII

Dans ce premier code, les caractères sont codés sur 7 bits auquel on ajoute, en début de code, un bit de parité qui vaut toujours « 0 », donnant finalement un codage caractère sur un octet, mais avec seulement 128 caractères possibles (27).

Dans la norme ASCII initiale, destinée au fonctionnement des ordinateurs aux USA, il n’était prévu aucun caractère accentué (inexistants en anglais). Ce n’est qu’en 1981 que la société IBM développe une extension du codage sur 8 bits pour ajouter les caractères accentués, ainsi que de nouveaux symboles. C’est ce code ASCII étendu qui sera utilisé dans les ordinateurs IBM-PC modèle 5150, considéré comme le premier ordinateur personnel.

En plus des caractères présents sur le clavier de l’ordinateur, l’accès aux autres caractères est possible en connaissant leur code d’identification. Par exemple « 128 » pour le c cédille « ç ». Sous Windows il est ainsi possible de maintenir la touche ALT et de taper au clavier numérique les chiffres « 128 » pour afficher le « ç ».

Le codage ASCII sur 8 bits est particulièrement économe en place (chaque caractère ne fait qu’un octet), mais est limité en nombres de symboles. Il convenait bien aux débuts de l’informatique, mais d’autres systèmes le supplantent désormais. Il reste, malgré tout, un standard qui est compris par tous les systèmes d’exploitation.

dec hex bin valeur dec hex bin Val dec hex bin val dec hex bin val
0 0 0 Null (NUL) 32 20 100000 Space 64 40 1000000 @ 96 60 1100000 `
1 1 1 Start of heading (SOH) 33 21 100001 ! 65 41 1000001 A 97 61 1100001 a
2 2 10 Start of text (STX) 34 22 100010 66 42 1000010 B 98 62 1100010 b
3 3 11 End of text (ETX) 35 23 100011 # 67 43 1000011 C 99 63 1100011 c
4 4 100 End of transmission (EOT) 36 24 100100 $ 68 44 1000100 D 100 64 1100100 d
5 5 101 Enquiry (ENQ) 37 25 100101 % 69 45 1000101 E 101 65 1100101 e
6 6 110 Acknowledge (ACK) 38 26 100110 & 70 46 1000110 F 102 66 1100110 f
7 7 111 Bell (BEL) 39 27 100111 71 47 1000111 G 103 67 1100111 g
8 8 1000 Backspace (BS) 40 28 101000 ( 72 48 1001000 H 104 68 1101000 h
9 9 1001 Horizontal tab (HT) 41 29 101001 ) 73 49 1001001 I 105 69 1101001 i
10 0A 1010 Line feed (LF) 42 2A 101010 * 74 4A 1001010 J 106 6A 1101010 j
11 0B 1011 Vertical tab (VT) 43 2B 101011 + 75 4B 1001011 K 107 6B 1101011 k
12 0C 1100 New page/form feed (FF) 44 2C 101100 , 76 4C 1001100 L 108 6C 1101100 l
13 0D 1101 Carriage return (CR) 45 2D 101101 77 4D 1001101 M 109 6D 1101101 m
14 0E 1110 Shift out (SO) 46 2E 101110 . 78 4E 1001110 N 110 6E 1101110 n
15 0 F 1111 Shift in (SI) 47 2 F 101111 / 79 4 F 1001111 O 111 6 F 1101111 o
16 10 10000 Data link escape (DLE) 48 30 110000 0 80 50 1010000 P 112 70 1110000 p
17 11 10001 Device control 1 (DC1) 49 31 110001 1 81 51 1010001 Q 113 71 1110001 q
18 12 10010 Device control 2 (DC2) 50 32 110010 2 82 52 1010010 R 114 72 1110010 r
19 13 10011 Device control 3 (DC3) 51 33 110011 3 83 53 1010011 S 115 73 1110011 s
20 14 10100 Device control 4 (DC4) 52 34 110100 4 84 54 1010100 T 116 74 1110100 t
21 15 10101 Negative acknowledge (NAK) 53 35 110101 5 85 55 1010101 U 117 75 1110101 u
22 16 10110 Synchronous idle (SYN) 54 36 110110 6 86 56 1010110 V 118 76 1110110 v
23 17 10111 End of transmission block (ETB) 55 37 110111 7 87 57 1010111 W 119 77 1110111 w
24 18 11000 Cancel (CAN) 56 38 111000 8 88 58 1011000 X 120 78 1111000 x
25 19 11001 End of medium (EM) 57 39 111001 9 89 59 1011001 Y 121 79 1111001 y
26 1A 11010 Substitute (SUB) 58 3A 111010 : 90 5A 1011010 Z 122 7A 1111010 z
27 1B 11011 Escape (ESC) 59 3B 111011 ; 91 5B 1011011 [ 123 7B 1111011 {
28 1C 11100 File separator (FS) 60 3C 111100 <  92 5C 1011100 \ 124 7C 1111100 |
29 1D 11101 Group separator (GS) 61 3D 111101 = 93 5D 1011101 ] 125 7D 1111101 }
30 1E 11110 Record separator (RS) 62 3E 111110 >  94 5E 1011110 ^ 126 7E 1111110 ~
31 1 F 11111 Unit separator (US) 63 3 F 111111 ? 95 5 F 1011111 _ 127 7 F 1111111 DEL

Autres normes : Unicode et ISO-8859-1

Avec le développement de l’informatique dans le monde entier dans les années 1980, il devient rapidement nécessaire de prévoir l’écriture dans d’autres langues que l’anglais et dans d’autres caractères que ceux utilisés en occident. Le développement d’une nouvelle norme universelle de codage de caractères aboutit, en 1988, à « Unicode », un codage des caractères sur 16 bits (65536 caractères possibles), compatible avec ASCII. Une première version, publiée sous la version 1.0 en 1991, inclut 7163 caractères et couvre des écritures comme l’arabe, le cyrillique, le grec, l’hébreu ou le thaï.

En 1996, la version 2.0 d’Unicode permet le codage sur plus de 16 bits afin de pouvoir incorporer également les langues anciennes, comme les hiéroglyphes, et, plus tard, les emoji. En 2020 il y a 143859 caractères dans la version 13.0 de l’Unicode.

Malgré le développement de l’Unicode, c’est une autre norme, nommée « ISO-8859-1 », publiée en 1987, qui sera utilisée dans les premiers échanges sur Internet. Cette norme de codage sur 8 bits, basé sur l’alphabet latin (correspondant aux langues occidentales) sera le codage par défaut en HTML jusqu’à la version HTML 4 qui bascule sur le codage Unicode UTF-8.

La norme ISO-8859-1 n’est compatible totalement qu’avec la première partie de la table ASCII, sur 7 bit, mais pas avec la table étendue. De ce fait, lors du passage d’un texte encodé en ISO-8859-1 vers ASCII étendu (et inversement), les caractères accentués sont remplacés par d’autres, rendant les documents difficiles à lire.

UTF-8, pour « Universal Transformation Format 8 » est la façon de coder les caractères de la table Unicode la plus utilisée dans le monde. Cette norme d’encodage est pleinement compatible avec ASCII et permet de coder les caractères sur un nombre variable de 1 à 4 octets. Cette norme est celle qui est utilisée en Python pour les chaînes de caractères. En HTML, il est prudent de déclarer l’utilisation de cette norme dans l’en-tête de la page (HEAD) avec le code :

<meta charset=”utf-8”>

Passer d’un codage à un autre

Dans la norme UTF-8, les bits de poids fort du premier octet (à gauche) vont indiquer le nombre d’octets utilisés pour le caractère en mettant un « 1 » pour chaque octet à partir de 2. Tous les octets suivants commencent par la séquence « 10 » :

  • Codage sur 1 octet : « 0x xx xx xx » correspond à la table ASCII de base sur 7bit.
  • 2 octets : « 11 0x xx xx 10 xx xx xx »
  • 3 octets : « 11 10 xx xx  10 xx xx xx     10 xx xx xx »
  • 4 octets : « 11 11 0x xx 10 xx xx xx     10 xx xx xx     10 xx xx xx  »

Les codages sur 2,3 ou 4 octets permettent de couvrir tous les caractères connus.

Le numéro correspondant à chaque caractère est généralement donné en hexadécimal avec l’écriture « U+xxxx » ou « xxxx » est un nombre hexadécimal avec les plages suivantes :

  • 1 octet : U+0000 à U+007F
  • 2 octets : U+0080 à U+07FF
  • 3 octets : U+0800 à U+FFFF
  • 4 octets : U+10000 à U+10FFFF

En UTF-8, le caractère mathématique « ⊕ » est encodé avec « U+2295 ». En HTML il est possible d’utiliser l’écriture « &#x2295 » pour coder ce caractère dans une page web (à condition d’avoir déclaré le charset UTF-8 comme vu plus haut).

L’intérêt d’utiliser UTF-8 est donc d’assurer une plus grande compatibilité aux documents textes. Il faut toutefois toujours faire attention à l’encodage utilisé par défaut, car un texte encodé en ISO-8859-1 (encore utilisé par certains « anciens » logiciels) ne sera pas affiché correctement en UTF-8.

Chapitre précédent

Chapitre suivant