Aller au contenu

Fonctionnement d'un ordinateur/Le codage des nombres

Un livre de Wikilivres.

Dans le chapitre précédent, nous avons vu que les ordinateurs actuels utilisent un codage binaire. Ce codage binaire ne vous est peut-être pas familier. Aussi, dans ce chapitre, nous allons apprendre comment coder des nombres en binaire. Nous allons commencer par le cas le plus simple : les nombres positifs. Par la suite, nous aborderons les nombres négatifs. Et nous terminerons par les nombres à virgules, appelés aussi nombres flottants.

Le codage des nombres entiers positifs

[modifier | modifier le wikicode]

Pour coder des nombres entiers positifs, il existe plusieurs méthodes : le binaire, l’hexadécimal, le code Gray, le décimal codé binaire et bien d'autres encore. La plus connue est certainement le binaire, secondée par l'hexadécimal, les autres étant plus anecdotiques. Pour comprendre ce qu'est le binaire, il nous faut faire un rappel sur les nombres entiers tel que vous les avez appris en primaire, à savoir les entiers écrits en décimal. Prenons un nombre écrit en décimal : le chiffre le plus à droite est le chiffre des unités, celui à côté est pour les dizaines, suivi du chiffre des centaines, et ainsi de suite. Dans un tel nombre :

  • on utilise une dizaine de chiffres, de 0 à 9 ;
  • chaque chiffre est multiplié par une puissance de 10 : 1, 10, 100, 1000, etc. ;
  • la position d'un chiffre dans le nombre indique par quelle puissance de 10 il faut le multiplier : le chiffre des unités doit être multiplié par 1, celui des dizaines par 10, celui des centaines par 100, et ainsi de suite.

Exemple avec le nombre 1337 :

Pour résumer, un nombre en décimal s'écrit comme la somme de produits, chaque produit multipliant un chiffre par une puissance de 10. On dit alors que le nombre est en base 10.

Ce qui peut être fait avec des puissances de 10 peut être fait avec des puissances de 2, 3, 4, 125, etc : n'importe quel nombre entier strictement positif peut servir de base. En informatique, on utilise rarement la base 10 à laquelle nous sommes tant habitués. On utilise à la place deux autres bases :

  • La base 2 (système binaire) : les chiffres utilisés sont 0 et 1 ;
  • La base 16 (système hexadécimal) : les chiffres utilisés sont 0, 1, 2, 3, 4, 5, 6, 7, 8 et 9 ; auxquels s'ajoutent les six premières lettres de notre alphabet : A, B, C, D, E et F.

Le système binaire

[modifier | modifier le wikicode]

En binaire, on compte en base 2. Cela veut dire qu'au lieu d'utiliser des puissances de 10 comme en décimal, on utilise des puissances de deux : n'importe quel nombre entier peut être écrit sous la forme d'une somme de puissances de 2. Par exemple 6 s'écrira donc 0110 en binaire : . On peut remarquer que le binaire n'autorise que deux chiffres, à savoir 0 ou 1 : ces chiffres binaires sont appelés des bits (abréviation de Binary Digit). Pour simplifier, on peut dire qu'un bit est un truc qui vaut 0 ou 1. Pour résumer, tout nombre en binaire s'écrit sous la forme d'un produit entre bits et puissances de deux de la forme :

Les coefficients sont les bits, l'exposant n qui correspond à un bit est appelé le poids du bit.

La terminologie du binaire

[modifier | modifier le wikicode]

En informatique, il est rare que l'on code une information sur un seul bit. Dans la plupart des cas, l'ordinateur manipule des nombres codés sur plusieurs bits. Les informaticiens ont donné des noms aux groupes de bits suivant leur taille. Le plus connu est certainement l'octet, qui désigne un groupe de 8 bits. Moins connu, on parle de nibble pour un groupe de 4 bits (un demi-octet), de doublet pour un groupe de 16 bits (deux octets) et de quadruplet pour un groupe de 32 bits (quatre octets).

Précisons qu'en anglais, le terme byte n'est pas synonyme d'octet. En réalité, le terme octet marche aussi bien en français qu'en anglais. Quant au terme byte, il désigne un concept complètement différent, que nous aborderons plus tard (c'est la plus petite unité de mémoire que le processeur peut adresser). Il a existé dans le passé des ordinateurs où le byte faisait 4, 7, 9, 16, voire 48 bits, par exemple. Il a même existé des ordinateur où le byte faisait exactement 1 bit ! Mais sur presque tous les ordinateurs modernes, un byte fait effectivement 8 bits, ce qui fait que le terme byte est parfois utilisé en lieu et place d'octet. Mais c'est un abus de langage, attention aux confusions ! Dans ce cours, nous parlerons d'octet pour désigner un groupe de 8 bits, en réservant le terme byte pour sa véritable signification.

À l'intérieur d'un nombre, le bit de poids faible est celui qui est le plus à droite du nombre, alors que le bit de poids fort est celui non nul qui est placé le plus à gauche, comme illustré dans le schéma ci-dessous.

Bit de poids fort.
Bit de poids faible.

Cette terminologie s'applique aussi pour les bits à l'intérieur d'un octet, d'un nibble, d'un doublet ou d'un quadruplet. Pour un nombre codés sur plusieurs octets, on peut aussi parler de l'octet de poids fort et de l'octet de poids faible, du doublet de poids fort ou de poids faible, etc.

La traduction binaire→décimal

[modifier | modifier le wikicode]

Pour traduire un nombre binaire en décimal, il faut juste se rappeler que la position d'un bit indique par quelle puissance il faut le multiplier. Ainsi, le chiffre le plus à droite est le chiffre des unités : il doit être multiplié par 1 (). Le chiffre situé immédiatement à gauche du chiffre des unités doit être multiplié par 2 (). Le chiffre encore à gauche doit être multiplié par 4 (), et ainsi de suite. Mathématiquement, on peut dire que le énième bit en partant de la droite doit être multiplié par . Par exemple, la valeur du nombre noté 1011 en binaire est de .

Value of digits in the Binary numeral system

La traduction décimal→binaire

[modifier | modifier le wikicode]

La traduction inverse, du décimal au binaire, demande d'effectuer des divisions successives par deux. Les divisions en question sont des divisions euclidiennes, avec un reste et un quotient. En lisant les restes des divisions dans un certain sens, on obtient le nombre en binaire. Voici comment il faut procéder, pour traduire le nombre 34 :

Exemple d'illustration de la méthode de conversion décimal vers binaire.

Quelques opérations en binaire

[modifier | modifier le wikicode]

Maintenant que l'on sait coder des nombres en binaire normal, il est utile de savoir comment faire quelques opérations usuelles en binaire. Nous utiliserons les acquis de cette section dans la suite du chapitre, bien que de manière assez marginale.

La première opération est assez spécifique au binaire. Il s'agit d'une opération qui inverse les bits d'un nombre : les 0 deviennent des 1 et réciproquement. Par exemple, le nombre 0001 1001 devient 1110 0110. Elle porte plusieurs noms : opération NOT, opération NON, complémentation, etc. Nous parlerons de complémentation ou d'opération NOT dans ce qui suit. Beaucoup d'ordinateurs gèrent cette opération, ils savent la faire en un seul calcul. Il faut dire que c'est une opération assez utile, bien que nous ne pouvons pas encore expliquer pourquoi.

Exemple d'addition en binaire.

La seconde opération à aborder est l'addition. Elle se fait en binaire de la même manière qu'en décimal : Pour faire une addition en binaire, on additionne les chiffres/bits colonne par colonne, une éventuelle retenue est propagée à la colonne d'à côté. Sauf que l'on additionne des bits. Heureusement, la table d'addition est très simple en binaire :

  • 0 + 0 = 0, retenue = 0 ;
  • 0 + 1 = 1, retenue = 0 ;
  • 1 + 0 = 1, retenue = 0 ;
  • 1 + 1 = 0, retenue = 1.

La troisième opération est une variante de l'addition appelée l'opération XOR, notée . Il s'agit d'une addition dans laquelle on ne propage pas les retenues. L'addition des deux bits des opérandes se fait normalement, mais les retenues sont simplement oubliées, on n'en tient pas compte. Le résultat est que l'addition se résume à appliquer la table d'addition précédente :

  • 0 0 = 0 ;
  • 0 1 = 1 ;
  • 1 0 = 1 ;
  • 1 1 = 0.

Pour résumer, le résultat vaut 1 si les deux bits sont différents, 0 s'ils sont identiques. L'opération XOR sera utilisée rapidement dans le chapitre suivant, quand nous parlerons rapidement du mot de parité. Et elle sera beaucoup utilisée dans la suite du cours, nous en feront fortement usage. Pour le moment, mémorisez juste cette opération, elle n'a rien de compliqué.

Une dernière opération est l'opération de population count. Il s'agit ni plus ni moins que de compter le nombre de bits qui sont à 1 dans un nombre. Par exemple, pour le nombre 0110 0010 1101 1110, elle donne pour résultat 9. Elle est utilisée dans certaines applications, comme le calcul de certains codes correcteurs d'erreur, comme on le verra dans le chapitre suivant. Elle est supportée sur de nombreux ordinateurs, encore que cela dépende du processeur considéré. Il s'agit cependant d'une opération assez courante, supportée par les processeurs ARM, les processeurs x86 modernes (ceux qui gèrent le SSE), et quelques autres.

L'hexadécimal

[modifier | modifier le wikicode]

L’hexadécimal est basé sur le même principe que le binaire, sauf qu'il utilise les 16 chiffres suivants :

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

Dans les textes, afin de différencier les nombres décimaux des nombres hexadécimaux, les nombres hexadécimaux sont suivis par un petit h, indiqué en indice. Si cette notation n'existait pas, des nombres comme 2546 seraient ambigus : on ne saurait pas dire sans autre indication s'ils sont écrits en décimal ou en hexadécimal. Avec la notation, on sait de suite que 2546 est en décimal et que 2546h est en hexadécimal.

Dans les codes sources des programmes, la notation diffère selon le langage de programmation. Certains supportent le suffixe h pour les nombres hexadécimaux, d'autres utilisent un préfixe 0x ou 0h.

La conversion hexadécimal↔décimal

[modifier | modifier le wikicode]

Pour convertir un nombre hexadécimal en décimal, il suffit de multiplier chaque chiffre par la puissance de 16 qui lui est attribuée. Là encore, la position d'un chiffre indique par quelle puissance celui-ci doit être multiplié : le chiffre le plus à droite est celui des unités, le second chiffre le plus à droite doit être multiplié par 16, le troisième chiffre en partant de la droite doit être multiplié par 256 (16 * 16) et ainsi de suite. La technique pour convertir un nombre décimal vers de l’hexadécimal est similaire à celle utilisée pour traduire un nombre du décimal vers le binaire. On retrouve une suite de divisions successives, mais cette fois-ci les divisions ne sont pas des divisions par 2 : ce sont des divisions par 16.

La conversion hexadécimal↔binaire

[modifier | modifier le wikicode]

La conversion inverse, de l'hexadécimal vers le binaire est très simple, nettement plus simple que les autres conversions. Pour passer de l'hexadécimal au binaire, il suffit de traduire chaque chiffre en sa valeur binaire, celle indiquée dans le tableau au tout début du paragraphe nommé « Hexadécimal ». Une fois cela fait, il suffit de faire le remplacement. La traduction inverse est tout aussi simple : il suffit de grouper les bits du nombre par 4, en commençant par la droite (si un groupe est incomplet, on le remplit avec des zéros). Il suffit alors de remplacer le groupe de 4 bits par le chiffre hexadécimal qui correspond.

Interlude propédeutique : la capacité d'un entier et les débordements d'entiers

[modifier | modifier le wikicode]

Dans la section précédente, nous avons vu comment coder des entiers positifs en binaire ou dans des représentations proches. La logique voudrait que l'on aborde ensuite le codage des entiers négatifs. Mais nous allons déroger à cette logique simple, pour des raisons pédagogiques. Nous allons faire un interlude qui introduira des notions utiles pour la suite du chapitre. De plus, ces concepts seront abordés de nombreuses fois dans ce wikilivre et l'introduire ici est de loin la solution idéale.

Les ordinateurs manipulent des nombres codés sur un nombre fixe de bits

[modifier | modifier le wikicode]

Vous avez certainement déjà entendu parler de processeurs 32 ou 64 bits. Et si vous avez joué aux jeux vidéos durant votre jeunesse et êtes assez agé, vous avez entendu parler de consoles de jeu 8 bits, 16 bits, 32 bits, voire 64 bits (pour la Jaguar, et c'était un peu trompeur). Derrière cette appellation qu'on retrouvait autrefois comme argument commercial dans la presse se cache un concept simple. Tout ordinateur manipule des nombres entiers dont le nombre de bits est toujours le même : on dit qu'ils sont de taille fixe. Une console 16 bits manipulait des entiers codés en binaire sur 16 bits, pas un de plus, pas un de moins. Pareil pour les anciens ordinateurs 32 bits, qui manipulaient des nombres entiers codés sur 32 bits.

Aujourd'hui, les ordinateurs modernes utilisent presque un nombre de bits qui est une puissance de 2 : 8, 16, 32, 64, 128, 256, voire 512 bits. Mais cette règle souffre évidemment d'exceptions. Aux tout débuts de l'informatique, certaines machines utilisaient 3, 7, 13, 17, 23, 36 et 48 bits ; mais elles sont aujourd'hui tombées en désuétude. De nos jours, il ne reste que les processeurs dédiés au traitement de signal audio, que l'on trouve dans les chaînes HIFI, les décodeurs TNT, les lecteurs DVD, etc. Ceux-ci utilisent des nombres entiers de 24 bits, car l'information audio est souvent codée par des nombres de 24 bits.

Anecdote amusante, il a existé des ordinateurs de 1 bit, qui sont capables de manipuler des nombres codés sur 1 bit, pas plus. Un exemple est le Motorola MC14500B, commercialisé de 1976.

Le lien entre nombre de bits et valeurs codables

[modifier | modifier le wikicode]

Évidemment, on ne peut pas coder tous les entiers possibles et imaginables avec seulement 8 bits, ou 16 bits. Et il parait intuitif que l'on ait plus de valeurs codables sur 16 bits qu'avec 8 bits, par exemple. Plus le nombre de bits est important, plus on pourra coder de valeurs entières différentes. Mais combien de plus ? Par exemple, si je passe de 8 bits à 16 bits, est-ce que le nombre de valeurs que l'on peut coder double, quadruple, pentuple ? De même, combien de valeurs différentes on peut coder avec bits. Par exemple, combien de nombres différents peut-on coder avec 4, 8 ou 16 bits ? La section précédente vous l'expliquer.

Avec bits, on peut coder valeurs différentes, dont le , ce qui fait qu'on peut compter de à . N'oubliez pas cette formule : elle sera assez utile dans la suite de ce tutoriel. Pour exemple, on peut coder 16 valeurs avec 4 bits, qui vont de 0 à 15. De même, on peut coder 256 valeurs avec un octet, qui vont de 0 à 255. Le tableau ci-dessous donne quelques exemples communs.

Nombre de bits Nombre de valeurs codables
4 16
8 256
16 65 536
32 4 294 967 296
64 18 446 744 073 709 551 615

Inversement, on peut se demander combien de bits il faut pour coder une valeur quelconque, que nous noterons N. Pour cela, il faut utiliser la formule précédente, mais à l'envers. On cherche alors tel que . L'opération qui donne est appelée le logarithme, et plus précisément un logarithme en base 2, noté . Le problème est que le résultat du logarithme ne tombe juste que si le nombre X est une puissance de 2. Si ce n'est pas le cas, le résultat est un nombre à virgule, ce qui n'a pas de sens pratique. Par exemple, la formule nous dit que pour coder le nombre 13, on a besoin de 3,70043971814 bits, ce qui est impossible. Pour que le résultat ait un sens, il faut arrondir à l'entier supérieur. Pour l'exemple précédent, les 3,70043971814 bits s'arrondissent en 4 bits.

Le lien entre nombre de chiffres hexadécimaux et valeurs codables

[modifier | modifier le wikicode]

Maintenant, voyons combien de valeurs peut-on coder avec chiffres hexadécimaux. La réponse n'est pas très différente de celle obtenue en binaire, si ce n'est qu'il faut remplacer le 2 par un 16 dans la formule précédente. Avec chiffres hexadécimaux, on peut coder valeurs différentes, dont le , ce qui fait qu'on peut compter de à . Le tableau ci-dessous donne quelques exemples communs.

Nombre de chiffres héxadécimaux Nombre de valeurs codables
1 (4 bits) 16
2 (8 bits) 256
4 (16 bits) 65 536
8 (32 bits) 4 294 967 296
16 (64 bits) 18 446 744 073 709 551 615

Inversement, on peut se demander combien faut-il de chiffres hexadécimaux pour coder une valeur quelconque en hexadécimal. La formule est là encore la même qu'en binaire, sauf qu'on remplace le 2 par un 16. Pour trouver le nombre de chiffres hexadécimaux pour encoder un nombre X, il faut calculer . Notons que le logarithme utilisé est un logarithme en base 16, et non un logarithme en base 2, comme pour le binaire. Là encore, le résultat ne tombe juste que si le nombre X est une puissance de 16 et il faut arrondir à l'entier supérieur si ce n'est pas le cas.

Une propriété mathématique des logarithmes nous dit que l'on peut passer d'un logarithme en base X et à un logarithme en base Y avec une simple division, en utilisant la formule suivante :

Dans le cas qui nous intéresse, on a Y = 2 et X = 16, ce qui donne :

Or, est tout simplement égal à 4, car il faut 4 bits pour coder la valeur 16. On a donc :

En clair, il faut quatre fois moins de chiffres hexadécimaux que de bits, ce qui est assez intuitif vu qu'il faut 4 bits pour coder un chiffre hexadécimal.

Les débordements d'entier

[modifier | modifier le wikicode]

On vient de voir que tout ordinateur manipule des nombres dont le nombre de bits est toujours le même : on dit qu'ils sont de taille fixe. Et cela limite les valeurs qu'il peut encoder, qui sont comprises dans un intervalle bien précis. Mais que ce passe-t-il si jamais le résultat d'un calcul ne rentre pas dans cet intervalle ? Par exemple, pour du binaire normal, que faire si le résultat d'un calcul atteint ou dépasse  ? Dans ce cas, le résultat ne peut pas être représenté par l'ordinateur et il se produit ce qu'on appelle un débordement d'entier.

On peut imaginer d'autres codages pour lesquels les entiers ne commencent pas à zéro ou ne terminent pas à . On peut prendre le cas où l'ordinateur gère les nombres négatifs, par exemple. Dans le cas général, l'ordinateur peut coder les valeurs comprises dans un intervalle, qui va de la valeur la plus basse à la valeur la plus grande . Et encore une fois, si un résultat de calcul sort de cet intervalle, on fait face à un débordement d'entier.

La valeur haute de débordement désigne la première valeur qui est trop grande pour être représentée par l'ordinateur. Par exemple, pour un ordinateur qui peut coder tous les nombres entre 0 et 7, la valeur haute de débordement est égale à 8. Pour les nombres entiers, la valeur haute de débordement vaut , avec la plus grande valeur codable par l'ordinateur.

On peut aussi définir la valeur basse de débordement, qui est la première valeur trop petite pour être codée par l'ordinateur. Par exemple, pour un ordinateur qui peut coder tous les nombres entre 8 et 250, la valeur basse de débordement est égale à 7. Pour les nombres entiers, la valeur basse de débordement vaut , avec la plus petite valeur codable par l'ordinateur.

La gestion des débordements d'entiers

[modifier | modifier le wikicode]

Face à un débordement d'entier, l'ordinateur peut utiliser deux méthodes : l'arithmétique saturée ou l'arithmétique modulaire.

L'arithmétique saturée consiste à arrondir le résultat pour prendre la plus grande ou la plus petite valeur. Si le résultat d'un calcul dépasse la valeur haute de débordement, le résultat est remplacé par le plus grand entier supporté par l'ordinateur. La même chose est possible quand le résultat est inférieur à la plus petite valeur possible, par exemple lors d'une soustraction : l'ordinateur arrondit au plus petit entier possible.

Pour donner un exemple, voici ce que cela donne avec des entiers codés sur 4 bits, qui codent des nombres de 0 à 15. Si je fais le calcul 8 + 9, le résultat normal vaut 17, ce qui ne rentre pas dans l'intervalle. Le résultat est alors arrondi à 15. Inversement, si je fais le calcul 8 - 9, le résultat sera de -1, ce qui ne rentre pas dans l'intervalle : le résultat est alors arrondi à 0.

Exemple de débordement d'entier sur un compteur mécanique quelconque. L'image montre clairement que le compteur revient à zéro une fois la valeur maximale dépassée.

L'arithmétique modulaire est plus compliquée et c'est elle qui va nous intéresser dans ce qui suit. Pour simplifier, imaginons que l'on décompte à partir de zéro. Quand on arrive à la valeur haute de débordement, on recommence à compter à partir de zéro. L'arithmétique modulaire n'est pas si contre-intuitive et vous l'utilisez sans doute au quotidien. Après tout, c'est comme cela que l'on compte les heures, les minutes et les secondes. Quand on compte les minutes, on revient à 0 au bout de 60 minutes. Pareil pour les heures : on revient à zéro quand on arrive à 24 heures. Divers compteurs mécaniques fonctionnent sur le même principe et reviennent à zéro quand ils dépassent la plus grande valeur possible, l'image ci-contre en montrant un exemple.

Mathématiquement, l'arithmétique modulaire implique des divisions euclidiennes, celles qui donnent un quotient et un reste. Lors d'un débordement, le résultat s'obtient comme suit : on divise le nombre qui déborde par la valeur haute de débordement, et que l'on conserve le reste de la division. Au passage, l'opération qui consiste à faire une division et à garder le reste au lieu du quotient est appelée le modulo. Prenons l'exemple où l'ordinateur peut coder tous les nombres entre 0 et 1023, soit une valeur haute de débordement de 1024. Pour coder le nombre 4563, on fait le calcul 4563 / 1024. On obtient : . Le reste de la division est de 467 et c'est lui qui sera utilisé pour coder la valeur de départ, 4563. Le nombre 4563 est donc codé par la valeur 467 dans un tel ordinateur en arithmétique modulaire. Au passage, la valeur haute de débordement est toujours codée par un zéro dans ce genre d'arithmétique.

Les ordinateurs utilisent le plus souvent une valeur haute de débordement de , avec n le nombre de bits utilisé pour coder les nombres entiers positifs. En faisant cela, l'opération modulo devient très simple et revient à éliminer les bits de poids forts au-delà du énième bit. Par exemple, reprenons l'exemple d'un ordinateur qui code ses nombres sur 4 bits. Imaginons qu'il fasse le calcul , soit 1101 + 0011 = 1 0000 en binaire. Le résultat entraîne un débordement d'entier et l'ordinateur ne conserve que les 4 bits de poids faible. Cela donne : 1101 + 0011 = 0000. Comme autre exemple l'addition 1111 + 0010 ne donnera pas 17 (1 0001), mais 1 (0001).

L'avantage est que les calculs sont beaucoup plus simples avec cette méthode qu'avec les autres. L'ordinateur a juste à ne pas calculer les bits de poids fort. Pas besoin de faire une division pour calculer un modulo, pas besoin de corriger le résultat pour faire de l'arithmétique saturée.

Aparté : quelques valeurs particulières en binaire

[modifier | modifier le wikicode]

Plus haut, on a dit qu'avec n bits, on peut encoder toutes les valeurs allant de à . Ce simple fait permet de déterminer quelle est la valeur de certains entiers à vue d’œil. Dans ce qui va suivre, nous allons poser quelques bases que nous réutiliserons dans la suite du chapitre. Il s'agit de quelques trivias qui sont cependant assez utiles.

Le premier trivia concerne la valeur maximale  : elle est encodée par un nombre dont tous les bits sont à 1.

Le deuxième trivia concerne les nombres de la forme 000...000 1111 1111, à savoir des nombres dont les x bits de poids faible sont à 1 et tous les autres valent 0. Par définition, de tels nombres codent la plus grande valeur possible sur x bits, ce qui fait qu'ils valent .

Le troisième trivia concerne les nombres de la forme 11111...0000. En clair, des nombres où on a une suite consécutive de 1 dans les bits de poids fort et les x bits de poids faibles à 0. De tels nombres valent : . La preuve est assez simple : ils s'obtiennent en prenant la valeur maximale , et en soustrayant un nombre de la forme .

Valeurs particulières en binaire

Si on utilise l'arithmétique modulaire, la valeur n'est autre que la valeur de débordement haute pour les nombres stockés sur n bits, et se code comme le zéro (il faudrait 1 bit de plus pour stocker le 1 de poids fort de ). Les 1er et 3ème nombres évoqués dans les paragraphes précédents peuvent donc se passer de dans leur expression :

  • est codé 1111111111111111...111 (n bits à 1)
  • est codé 11111...111000..000000 (x bits à 0 précédés de n-x bits à 1)

Les nombres entiers négatifs

[modifier | modifier le wikicode]
Représentations signées, exemples sur 4 bits.

Passons maintenant aux entiers négatifs en binaire : comment représenter le signe moins ("-") avec des 0 et des 1 ? Eh bien, il existe plusieurs méthodes :

  • la représentation en signe-valeur absolue ;
  • la représentation en complément à un ;
  • la représentation en complément à deux;
  • la représentation par excès ;
  • la représentation dans une base négative ;
  • d'autres représentations encore moins utilisées que les autres.

La représentation en signe-valeur absolue

[modifier | modifier le wikicode]

La solution la plus simple pour représenter un entier négatif consiste à coder sa valeur absolue en binaire, et rajouter un bit qui précise si c'est un entier positif ou un entier négatif. Par convention, ce bit de signe vaut 0 si le nombre est positif et 1 s'il est négatif. On parle alors de représentation en signe-valeur absolue, aussi appelée représentation en signe-magnitude.

Avec cette technique, il y autant de nombres positifs que négatifs. Mieux : pour chaque nombre représentable en représentation signe-valeur absolue, son inverse l'est aussi. Ce qui fait qu'avec cette méthode, le zéro est codé deux fois : on a un -0, et un +0. Cela pose des problèmes lorsqu'on demande à notre ordinateur d'effectuer des calculs ou des comparaisons avec zéro.

Codage sur 4 bits en signe-valeur absolue

La représentation en complément à un

[modifier | modifier le wikicode]

La représentation en complément à un peut être vue, en première approximation, comme une variante de la représentation en signe-magnitude. Et je dis bien : "em première approximation", car il y a beaucoup à dire sur la représentation en complément à un, mais nous verrons cela dans la section suivante sur le complément à deux. Conceptuellement, la représentation en signe-magnitude et celle du complément à un sont drastiquement différentes et sont basées sur des concepts mathématiques totalement différents. Mais elles ont des ressemblances de surface, qui font que faire la comparaison entre les deux est assez utile pédagogiquement parlant.

En complément à un, les nombres sont codés en utilisant un bit de signe qui indique si le nombre de positif ou négatif, couplé à une valeur absolue. Par contre, la valeur absolue est codée différemment en binaire. Pour les nombres positifs, la valeur absolue est codée en binaire normal, pas de changement comparé aux autres représentations. Mais pour les valeurs négatives, la valeur absolue est codée en binaire normal, puis tous les bits sont inversés : les 0 deviennent des 1 et réciproquement. En clair, on utilise une opération de complémentation pour les nombres négatifs.

Prenons un exemple avec un nombre codé sur 4 bits, avec un cinquième bit de signe. La valeur 5 est codée comme suit : 0 pour le bit de signe, 5 donne 0101 en binaire, le résultat est 0 0101. Pour la valeur -5, le bit de signe est 1, 5 donne 0101 en binaire, on inverse les bits ce qui donne 1010 : cela donne 11010. Notez qu'on peut passer d'un résultat à l'autre avec une opération de complémentation, à savoir en inversant les bits de l'autre et réciproquement.

Il s'agit là d'une propriété générale avec le complément à 1 : l'opposé d'un nombre, à savoir passer de sa valeur positive à sa valeur négative ou inversement, se calcule avec une opération NOT, une opération de complémentation (d'où son nom). L'avantage est que les ordinateurs gèrent naturellement d'opération de complémentation. Par contre, en signe-magnitude, inverser le bit de signe est une opération spécifique et ne sert qu'à ça. Il faut donc rajouter une opération en plus pour calculer l'opposé d'un nombre.

La représentation en complément à un garde les défauts de la représentation en signe-magnitude. Le zéro est codé deux fois, avec un zéro positif et un zéro négatif. La différence est que les valeurs négatives sont dans l'ordre inverse, il y a une symétrie un peu meilleure. Les deux zéros sont d'ailleurs totalement éloignés, ils correspondent aux valeurs extrêmes encodables : le zéro positif a tous ses bits à 0, le zéro négatif a tous ses bits à 1. Le résultat est que l'implémentation des comparaisons et de certains calculs est plus complexe qu'avec le signe-magnitude, mais de peu.

Codage sur 4 bits en complément à 1.

La représentation par excès

[modifier | modifier le wikicode]

La représentation par excès consiste à ajouter un biais aux nombres à encoder afin de les encoder par un entier positif. Pour encoder tous les nombres compris entre -X et Y en représentation par excès, il suffit de prendre la valeur du nombre à encoder, et de lui ajouter un biais égal à X. Ainsi, la valeur -X sera encodée par zéro, et toutes les autres valeurs le seront par un entier positif, le zéro sera encodé par X, 1, par X+1, etc. Par exemple, prenons des nombres compris entre -127 et 128. On va devoir ajouter un biais égal à 127, ce qui donne :

Valeur avant encodage Valeur après encodage
-127 0
-126 1
-125 2
0 127
127 254
128 255

La représentation en complément à deux

[modifier | modifier le wikicode]

La représentation en complément à deux est basée sur les mêmes mathématiques que le complément à un, mais fonctionne très différemment en pratique. Si on regarde de loin, son principe est assez différent : il n'y a pas de bit de signe ni quoi que ce soit d'autre. A la place, un nombre en complément à deux est encodé comme en binaire normal, à un point près : le bit de poids fort est soustrait, et non additionné aux autres. Il a une valeur négative : on soustrait la puissance de deux associée s'il vaut 1, on ne la tient pas en compte s'il vaut 0.

Par exemple, la valeur du nombre noté 111001 en complément à deux s'obtient comme suit :

-32 16 8 4 2 1
1 1 1 0 0 1

Sa valeur est ainsi de (−32×1)+(16×1)+(8×1)+(4×0)+(2×1)+(1×1) = −32+16+8+1 = -7.

Avec le complément à deux, comme avec le complément à un, le bit de poids fort vaut 0 pour les nombres positifs et 1 pour les négatifs.

L'avantage de cette représentation est qu'elle n'a pas de double zéro : le zéro n'est encodé que par une seule valeur. Par contre, la valeur autrefois prise par le zéro négatif est réutilisée pour encoder une valeur négative. La conséquence est que l'on a un nombre négatif en plus d'encodé, il n'y a plus le même nombre de valeurs strictement positives et de valeurs négatives encodées. Le nombre négatif en question est appelé le nombre le plus négatif, ce nom trahit le fait que c'est celui qui a la plus petite valeur (la plus grande valeur absolue). Il est impossible de coder l'entier positif associé, sa valeur absolue.

Codage sur 4 bits en complément à 2.

Le calcul du complément à deux : première méthode

[modifier | modifier le wikicode]

Les représentations en complément à un et en complément à deux sont basées sur le même principe mathématique. Leur idée est de remplacer chaque nombre négatif par un nombre positif équivalent, appelé le complément. Par équivalent, on veut dire que tout calcul donne le même résultat si on remplace un nombre négatif par son complément (idem avec un nombre positif, son complément aura un signe inverse). Et pour faire cela, elles se basent sur les débordements d'entier pour fonctionner, et plus précisément sur l'arithmétique modulaire abordée plus haut.

Si on fait les calculs avec le complément, les résultats du calcul entraînent un débordement d'entier, qui sera résolu par un modulo : l'ordinateur ne conserve que les bits de poids faible du résultat, les autres bits sont oubliés. Par exemple, prenons l'addition 15 + 2, 1111 + 0010 en binaire : le résultat ne sera pas 17 (10001), vu qu'on n'a pas assez de bits pour encoder le résultat, mais 1 (0001). Et le résultat après modulo sera identique au résultat qu'on aurait obtenu avec le nombre négatif sans modulo. En clair, c'est la gestion des débordements qui permet de corriger le résultat de manière à ce que l'opération avec le complément donne le même résultat qu'avec le nombre négatif voulu. Ainsi, on peut coder un nombre négatif en utilisant son complément positif.

Cela ressemble beaucoup à la méthode de soustraction basée sur un complément à 9 pour ceux qui connaissent, sauf que c'est une version binaire qui nous intéresse ici.

Prenons un exemple, qui permettra d'introduire la suite. Encore une fois, on utilise un codage sur 4 bits dont la valeur haute de débordement est de 16. Prenons l'addition de 13 + 3 = 16. Avec l'arithmétique modulaire, 16 est équivalent à 0, ce qui donne : 13 + 3 = 0 ! On peut aussi reformuler en disant que 13 = -3, ou encore que 3 = -13. Dit autrement, 3 est le complément de -13 pour ce codage. Et ne croyez pas que ça marche uniquement dans cet exemple : cela se généralise assez rapidement à tout nombre négatif.

Prenons un nombre N dont un veut calculer le complément à deux K. Dans le cas général, on a :

, vu que en utilisant le modulo.

En réorganisant très légèrement les termes pour isoler K, on a :

La formule précédent permet de calculer la complément à deux assez simplement, en faisant le calcul à la main.

Avant de poursuivre, prenons un exemple très intéressant : le cas où N = -1. Son complément à deux vaut donc :

Le terme devrait vous rappeler quelque chose : il s'agit d'un nombre dont tous les bits sont à 1. En clair, le complément à deux de -1 est un nombre de la forme 1111...111. Et cela vaut quelque soit le nombre de bits n. La représentation de -1 est similaire, peu importe que l'on utilise des nombres de 16 bits, 32 bits, 64 bits, etc.

Voyons maintenant le cas des puissance de deux, par exemple 2, 4, 8, 16, etc. Leur complément à deux vaut :

Nous avions vu précédemment dans le chapitre que ces nombres sont de la forme 11111...0000. En clair, des nombres dont les x bits de poids faible sont à 0, et tous les autres bits sont à 1. Par exemple, le complément à deux de -2 est un nombre dont les bits sont à 1, sauf le bit de poids faible. De même, le complément à deux de -4 a tous ses bits à 1, sauf les deux bits de poids faible. Et le complément à deux de -8 a tous ses bits à 1, sauf les trois bits de poids faible. Pour résumer, tout nombre de la forme a tous ses bits à 1, sauf les x bits de poids faible qui sont à 0.

Exemple avec des nombres de 8 bits
-1 1111 1111
-2 1111 1110
-4 1111 1100
-8 1111 1000
-16 1111 0000
-32 1110 0000
-64 1100 0000
-128 1000 0000
0 / - 256 0000 0000

Le calcul du complément à deux : seconde méthode

[modifier | modifier le wikicode]

Il existe une seconde méthode pour calculer le complément à deux d'un nombre, la voici. Pour les nombres positifs, encodez-les comme en binaire normale. Pour les nombres négatifs, faites pareil, puis inversez tous les bits, avant d'ajouter un. La procédure est identique à celle du complément à un, sauf que l'on incrémente le résultat final. Et ce n'est pas une coïncidence, comme nous allons le voir immédiatement.

Pour comprendre pourquoi la méthode marche, repartons de la formule précédente . Elle peut se reformuler comme suit :

La valeur est par définition un nombre dont tous les bits sont à 1. À cette valeur, on soustrait un nombre dont certains bits sont à 1 et d'autres à 0. En clair, pour chaque colonne, on a deux possibilités : soit on doit faire la soustraction , soit la soustraction . Or, les règles de l'arithmétique binaire disent que et . En regardant attentivement, on se rend compte que le bit du résultat est l'inverse du bit de départ. De plus, les deux cas ne donnent pas de retenue : le calcul pour chaque bit n'influence pas les bits voisins.

Le terme est donc le complément à un du nombre N, un nombre égal à sa représentation en binaire dont tous les bits sont inversés. Notons le nombre formé en inversant tous les bits de N. On a alors :

En clair, le complément à deux s'obtient en prenant le complément à un et en ajoutant 1. Dit autrement, il faut prendre le nombre N, en inverser tous les bits et ajouter 1.

Une autre manière équivalente consiste à faire le calcul suivant :

On prend le nombre dont on veut le complément, on soustrait 1 et on inverse les bits.

Notons que tout ce qui a été dit plus haut marche aussi pour le complément à un, avec cependant une petite différence : la valeur haute de débordement n'est pas la même, ce qui change les calculs. Pour des nombres codés sur bits, la valeur haute de débordement est égale à en complément à deux, alors qu'elle est de en complément à un. De ce fait, la gestion des débordements est plus simple en complément à deux.

L'extension de signe

[modifier | modifier le wikicode]

Dans les ordinateurs, tous les nombres sont codés sur un nombre fixé et constant de bits. Ainsi, les circuits d'un ordinateur ne peuvent manipuler que des nombres de 4, 8, 12, 16, 32, 48, 64 bits, suivant la machine. Si l'on veut utiliser un entier codé sur 16 bits et que l'ordinateur ne peut manipuler que des nombres de 32 bits, il faut bien trouver un moyen de convertir le nombre de 16 bits en un nombre de 32 bits, sans changer sa valeur et en conservant son signe. Cette conversion d'un entier en un entier plus grand, qui conserve valeur et signe s'appelle l'extension de signe.

L'extension de signe des nombres positifs consiste à remplir les bits de poids fort avec des 0 jusqu’à arriver à la taille voulue : c'est la même chose qu'en décimal, où rajouter des zéros à gauche d'un nombre positif ne changera pas sa valeur. Pour les nombres négatifs, il faut remplir les bits à gauche du nombre à convertir avec des 1, jusqu'à obtenir le bon nombre de bits : par exemple, 1000 0000 (-128 codé sur 8 bits) donnera 1111 1111 1000 000 après extension de signe sur 16 bits. L'extension de signe d'un nombre codé en complément à 2 se résume donc en une phrase : il faut recopier le bit de poids fort de notre nombre à convertir à gauche de celui-ci jusqu’à atteindre le nombre de bits voulu.

L'explication plus simple tient dans la manière de coder le bit de poids fort. Prenons l'exemple de la conversion d'un entier de 5 bits en un entier de 8bits. Les 4 bits de poids faible ont un poids positif (on les additionne), alors que le bit de poids fort a un poids négatif. Le nombre encodé vaut : . La valeur encodée sur 4 bits reste la même après extension de signe, car les poids des bits de poids faible ne changent pas. Par contre, le bit de poids fort change. Sur 8 bits, la valeur -16 est encodée par un nombre de la forme 1111 0000. En remplaçant le bit de poids fort par sa valeur calculée sur plus de bits, on remarque que les bits de poids fort ont été remplacés par des 1.

La représentation négabinaire

[modifier | modifier le wikicode]

Enfin, il existe une dernière méthode, assez simple à comprendre, appelée représentation négabinaire. Dans cette méthode, les nombres sont codés non en base 2, mais en base -2. Oui, vous avez bien lu : la base est un nombre négatif. Dans les faits, la base -2 est similaire à la base 2 : il y a toujours deux chiffres (0 et 1), et la position dans un chiffre indique toujours par quelle puissance de 2 il faut multiplier, sauf qu'il faudra ajouter un signe moins une fois sur 2. Concrètement, les puissances de -2 sont les suivantes : 1, -2, 4, -8, 16, -32, 64, etc. En effet, un nombre négatif multiplié par un nombre négatif donne un nombre positif, ce qui fait qu'une puissance sur deux est négative, alors que les autres sont positives. Ainsi, on peut représenter des nombres négatifs, mais aussi des nombres positifs dans une puissance négative.

Par exemple, la valeur du nombre noté 11011 en base -2 s'obtient comme suit :

-32 16 -8 4 -2 1
1 1 1 0 1 1

Sa valeur est ainsi de (−32×1)+(16×1)+(−8×1)+(4×0)+(−2×1)+(1×1)=−32+16−8−2+1=−25.

Les nombres à virgule

[modifier | modifier le wikicode]

On sait donc comment sont stockés nos nombres entiers dans un ordinateur. Néanmoins, les nombres entiers ne sont pas les seuls nombres que l'on utilise au quotidien : il nous arrive d'en utiliser à virgule. Notre ordinateur n'est pas en reste : il est lui aussi capable de manipuler de tels nombres. Dans les grandes lignes, il peut utiliser deux méthodes pour coder des nombres à virgule en binaire : La virgule fixe et la virgule flottante.

Les nombres à virgule fixe

[modifier | modifier le wikicode]

La méthode de la virgule fixe consiste à émuler les nombres à virgule à partir de nombres entiers. Un nombre à virgule fixe est codé par un nombre entier proportionnel au nombre à virgule fixe. Pour obtenir la valeur de notre nombre à virgule fixe, il suffit de diviser l'entier servant à le représenter par le facteur de proportionnalité. Par exemple, pour coder 1,23 en virgule fixe, on peut choisir comme « facteur de conversion » 1000, ce qui donne l'entier 1230.

Généralement, les informaticiens utilisent une puissance de deux comme facteur de conversion, pour simplifier les calculs. En faisant cela, on peut écrire les nombres en binaire et les traduire en décimal facilement. Pour l'exemple, cela permet d'écrire des nombres à virgule en binaire comme ceci : 1011101,1011001. Et ces nombres peuvent se traduire en décimal avec la même méthode que des nombres entier, modulo une petite différence. Comme pour les chiffres situés à gauche de la virgule, chaque bit situé à droite de la virgule doit être multiplié par la puissance de deux adéquate. La différence, c'est que les chiffres situés à droite de la virgule sont multipliés par une puissance négative de deux, c'est à dire par , , , , , ...

Cette méthode est assez peu utilisée de nos jours, quoiqu'elle puisse avoir quelques rares applications relativement connue. Un bon exemple est celui des banques : les sommes d'argent déposées sur les comptes ou transférées sont codés en virgule fixe. Les sommes manipulées par les ordinateurs ne sont pas exprimées en euros, mais en centimes d'euros. Et c'est une forme de codage en virgule fixe dont le facteur de conversion est égal à 100. La raison de ce choix est que les autres méthodes de codage des nombres à virgule peuvent donner des résultats imprécis : il se peut que les résultats doivent être tronqués ou arrondis, suivant les opérandes. Cela n'arrive jamais en virgule fixe, du moins quand on se limite aux additions et soustractions.

Les nombres flottants

[modifier | modifier le wikicode]

Les nombres à virgule fixe ont aujourd'hui été remplacés par les nombres à virgule flottante, où le nombre de chiffres après la virgule est variable. Le codage d'un nombre flottant est basée sur son écriture scientifique. Pour rappel, en décimal, l’écriture scientifique d'un nombre consiste à écrire celui-ci comme un produit entre un nombre et une puissance de 10. Ce qui donne :

, avec

Le nombre est appelé le significande et il est compris entre 1 (inclus) et 10 (exclu). Cette contrainte garantit que l'écriture scientifique d'un nombre est unique, qu'il n'y a qu'une seule façon d'écrire un nombre en notation scientifique. Pour cela, on impose le nombre de chiffre à gauche de la virgule et le plus simple est que celui-ci soit égal à 1. Mais il faut aussi que celui-ci ne soit pas nul. En effet, si on autorise de mettre un 0 à gauche de la virgule, il y a plusieurs manières équivalentes d'écrire un nombre. Ces deux contraintes font que le significande doit être égal ou plus grand que 1, mais strictement inférieur à 10. Par contre, on peut mettre autant de décimales que l'on veut.

En binaire, c'est la même chose, mais avec une puissance de deux. Cela implique de modifier la puissance utilisée : au lieu d'utiliser une puissance de 10, on utilise une puissance de 2.

, avec

Le significande est aussi altéré, au même titre que la puissance, même si les contraintes sont similaires à celles en base 10. En effet, le nombre ne possède toujours qu'un seul chiffre à gauche de la virgule, comme en base 10. Vu que seuls deux chiffres sont possibles (0 et 1) en binaire, on s'attend à ce que le chiffre situé à gauche de la virgule soit un zéro ou un 1. Mais rappelons que le chiffre à gauche doit être non-nul, pour les mêmes raisons qu'en décimal. En clair, le significande a forcément un bit à 1 à gauche de la virgule. Pour récapituler, l'écriture scientifique binaire d'un nombre consiste à écrire celui-ci sous la forme :

, avec

La partie fractionnaire du nombre , qu'on appelle la mantisse.

Écriture scientifique (anglais).

Traduire un nombre en écriture scientifique binaire

[modifier | modifier le wikicode]

Pour déterminer l'écriture scientifique en binaire d'un nombre quelconque, la procédure dépend de la valeur du nombre en question. Tout dépend s'il est dans l'intervalle , au-delà de 2 ou en-dessous de 1.

  • Pour un nombre entre 1 (inclus) et 2 (exclu), il suffit de le traduire en binaire. Son exposant est 0.
  • Pour un nombre au-delà de 2, il faut le diviser par 2 autant de fois qu'il faut pour qu'il rentre dans l’intervalle . L'exposant est alors le nombre de fois qu'il a fallu diviser par 2.
  • Pour un nombre plus petit que 1, il faut le multiplier par 2 autant de fois qu'il faut pour qu'il rentre dans l’intervalle . L'exposant se calcule en prenant le nombre de fois qu'il a fallu multiplier par 2, et en prenant l'opposé (en mettant un signe - devant le résultat).

Le codage des nombres flottants et la norme IEEE 754

[modifier | modifier le wikicode]

Pour coder cette écriture scientifique avec des nombres, l'idée la plus simple est d'utiliser trois nombres, pour coder respectivement la mantisse, l'exposant et un bit de signe. Coder la mantisse implique que le bit à gauche de la virgule vaut toujours 1, mais nous verrons qu'il y a quelques rares exceptions à cette règle. Quelques nombres flottants spécialisés, les dénormaux, ne sont pas codés en respectant les règles pour le significande et ont un 0 à gauche de la virgule. Un bon exemple est tout simplement la valeur zéro, que l'on peut coder en virgule flottante, mais seulement en passant outre les règles sur le significande. Toujours est-il que le bit à gauche de la virgule n'est pas codé, que ce soit pour les flottants normaux ou les fameux dénormaux qui font exception. On verra que ce bit peut se déduire en fonction de l'exposant utilisé pour encoder le nombre à virgule, ce qui lui vaut le nom de bit implicite. L'exposant peut être aussi bien positif que négatif (pour permettre de coder des nombres très petits), et est encodé en représentation par excès sur n bits avec un biais égal à .

IEEE754 Format Général

Le standard pour le codage des nombres à virgule flottante est la norme IEEE 754. Cette norme va (entre autres) définir quatre types de flottants différents, qui pourront stocker plus ou moins de valeurs différentes.

Classe de nombre flottant Nombre de bits utilisés pour coder un flottant Nombre de bits de l'exposant Nombre de bits pour la mantisse Décalage
Simple précision 32 8 23 127
Double précision 64 11 52 1023
Double précision étendue 80 ou plus 15 ou plus 64 ou plus 16383 ou plus

IEEE754 impose aussi le support de certains nombres flottants spéciaux qui servent notamment à stocker des valeurs comme l'infini. Commençons notre revue des flottants spéciaux par les dénormaux, aussi appelés flottants dénormalisés. Ces flottants ont une particularité : leur bit implicite vaut 0. Ces dénormaux sont des nombres flottants où l'exposant est le plus petit possible. Le zéro est un dénormal particulier dont la mantisse est nulle. Au fait, remarquez que le zéro est codé deux fois à cause du bit de signe : on se retrouve avec un -0 et un +0.

Bit de signe Exposant Mantisse
0 ou 1 Valeur minimale (0 en binaire) Mantisse différente de zéro (dénormal strict) ou égale à zéro (zéro)

Fait étrange, la norme IEEE754 permet de représenter l'infini, aussi bien en positif qu'en négatif. Celui-ci est codé en mettant l'exposant à sa valeur maximale et la mantisse à zéro. Et le pire, c'est qu'on peut effectuer des calculs sur ces flottants infinis. Mais cela a peu d'utilité.

Bit de signe Exposant Mantisse
0 ou 1 Valeur maximale Mantisse égale à zéro

Mais malheureusement, l'invention des flottants infinis n'a pas réglé tous les problèmes. Par exemple, quel est le résultat de  ? Ou encore  ? Autant prévenir tout de suite : mathématiquement, on ne peut pas savoir quel est le résultat de ces opérations. Pour pouvoir résoudre ces calculs, il a fallu inventer un nombre flottant qui signifie « je ne sais pas quel est le résultat de ton calcul pourri ». Ce nombre, c'est NaN. NaN est l'abréviation de Not A Number, ce qui signifie : n'est pas un nombre. Ce NaN a un exposant dont la valeur est maximale, mais une mantisse différente de zéro. Pour être plus précis, il existe différents types de NaN, qui diffèrent par la valeur de leur mantisse, ainsi que par les effets qu'ils peuvent avoir. Malgré son nom explicite, on peut faire des opérations avec NaN, mais cela ne sert pas vraiment à grand chose : une opération arithmétique appliquée avec un NaN aura un résultat toujours égal à NaN.

Bit de signe Exposant Mantisse
0 ou 1 Valeur maximale Mantisse différente de zéro

Les arrondis et exceptions

[modifier | modifier le wikicode]

La norme impose aussi une gestion des arrondis ou erreurs, qui arrivent lors de calculs particuliers. En voici la liste :

Nom de l’exception Description
Invalid operation Opération qui produit un NAN. Elle est levée dans le cas de calculs ayant un résultat qui est un nombre complexe, ou quand le calcul est une forme indéterminée. Pour ceux qui ne savent pas ce que sont les formes indéterminées, voici en exclusivité la liste des calculs qui retournent NaN : , , , , .
Overflow Résultat trop grand pour être stocké dans un flottant. Le plus souvent, on traite l'erreur en arrondissant le résultat vers Image non disponible ;
Underflow Pareil que le précédent, mais avec un résultat trop petit. Le plus souvent, on traite l'erreur en arrondissant le résultat vers 0.
Division par zéro Le nom parle de lui-même. La réponse la plus courante est de répondre + ou - l'infini.
Inexact Le résultat ne peut être représenté par un flottant et on doit l'arrondir.

La gestion des arrondis pose souvent problème. Pour donner un exemple, on va prendre le nombre 0,1. En binaire, ce nombre s'écrit comme ceci : 0,1100110011001100... et ainsi de suite jusqu'à l'infini. Notre nombre utilise une infinité de décimales. Bien évidemment, on ne peut pas utiliser une infinité de bits pour stocker notre nombre et on doit impérativement l'arrondir. Comme vous le voyez avec la dernière exception, le codage des nombres flottants peut parfois poser problème : dans un ordinateur, il se peut qu'une opération sur deux nombres flottants donne un résultat qui ne peut être codé par un flottant. On est alors obligé d'arrondir ou de tronquer le résultat de façon à le faire rentrer dans un flottant. Pour éviter que des ordinateurs différents utilisent des méthodes d'arrondis différentes, on a décidé de normaliser les calculs sur les nombres flottants et les méthodes d'arrondis. Pour cela, la norme impose le support de quatre modes d'arrondis :

  • Arrondir vers + l'infini ;
  • vers - l'infini ;
  • vers zéro ;
  • vers le nombre flottant le plus proche.

Les nombres flottants logarithmiques

[modifier | modifier le wikicode]

Les nombres flottants logarithmiques sont une spécialisation des nombres flottants IEEE754, ou tout du moins une spécialisation des flottants écrits en écriture scientifique. Un nombre logarithmique est donc composé d'un bit de signe et d'un exposant, sans mantisse. La mantisse est totalement implicite : tous les flottants logarithmiques ont la même mantisse, qui vaut 1.

Pour résumer, il ne reste que l'exposant, qui est tout simplement le logarithme en base 2 du nombre encodé, d'où le nombre de codage flottant logarithmique donné à cette méthode. Attention toutefois : l'exposant est ici un nombre fractionnaire, codé en virgule fixe. Le choix d'un exposant fractionnaire permet de représenter pas mal de nombres de taille diverses.

Bit de signe Exposant
Représentation binaire 0 01110010101111
Représentation décimale + 1040,13245464

L'utilité de cette représentation est de simplifier certains calculs, comme les multiplications, divisions, puissances, etc. En effet, les mathématiques nous disent que le logarithme d'un produit est égal à la somme des logarithmes : . Or, il se trouve que les ordinateurs sont plus rapides pour faire des additions/soustractions que pour faire des multiplications/divisions. Donc, la représentation logarithmique permet de remplacer les multiplications/divisions par des additions/soustractions, plus simples et plus rapides pour l'ordinateur.

Évidemment, les applications des flottants logarithmiques sont rares, limitées à quelques situations bien précises (traitement d'image, calcul scientifique spécifiques).

Les nombres à virgule non-représentables en binaire

[modifier | modifier le wikicode]

Quel que soit la façon de représenter les nombres à virgules, il existe des nombres qui ne peuvent être représentés de manière exacte, à savoir avec un nombre fini de décimales après la virgule. En soi, ce n'est pas spécifique au binaire, on a la même chose en décimal. Par exemple, la fraction 1/3 en décimal s'écrit 0.3333333..., avec une infinité de 3. La même chose existe en binaire, mais pour des nombres différents.

Déjà, évacuons le cas des nombres irrationnels, à savoir les nombres qui ne peuvent pas s'écrire sous la forme d'une fraction, comme ou . Ils ont une infinité de décimales que ce soit en binaire, en décimal, en hexadécimal, ou autre. Ils ne sont pas représentables avec un nombre fini de décimales quelle que soit la base utilisée. Concentrons-nous sur des nombres qui ne sont pas dans ce cas, et qui ont un nombre fini ou infini de décimales.

Le passage de la base 10 à la base 2 change le nombre de décimales, et peut faire passer d'un nombre fini de décimales à un nombre infini. Par exemple, est représenté en binaire avec une séquence infinie de bits : . Les nombres étant en base binaire et représenté avec un nombre limité de bits, il existe certains nombres décimaux triviaux qui ne sont pas représentables avec un nombre fini de décimales.

Et la réciproque n'est pas vraie : tout nombre binaire avec un nombre fini de décimales en binaire est représentable avec un nombre fini de décimales en base 10. Ceci est lié à la décomposition en facteur premier des bases utilisées :

Le nombre 10 possède tous les facteurs premiers de 2, mais 2 n'a pas de 5 dans sa décomposition.

Les encodages hybrides entre décimal et binaire

[modifier | modifier le wikicode]

Dans cette section, nous allons voir les représentations qui mixent binaire et décimal. Il s'agit en réalité d'encodage qui permettent de manipuler des nombres codés en décimal, mais les chiffres sont codés en utilisant des bits. L'encodage Binary Coded Decimal est le plus connu de cette catégorie, mais il y en a quelques autres, qui sont moins connus. De telles représentations étaient très utilisées au début de l'informatique, mais sont aujourd'hui tombées en désuétude. Pour désigner ces encodages, nous parlerons d'encodages bit-décimaux : décimaux pour préciser qu'ils encodent des nombres codés en décimal, bit pour préciser que les chiffres sont codés avec des bits.

De tels encodages étaient utilisés sur les tous premiers ordinateurs pour faciliter le travail des programmeurs, mais aussi sur les premières calculettes. Ils sont utiles dans les applications où on doit manipuler chaque chiffre décimal séparément des autres. Un exemple classique est celui d'une horloge digitale. Ils ont aussi l'avantage de bien se marier avec les nombres à virgule. Les représentations des nombres à virgule fixe ou flottante ont le défaut que des arrondis peuvent survenir. Par exemple, la valeur 0,2 est codée comme suit en binaire normal : 0.00110011001100... et ainsi de suite jusqu’à l'infini. Avec les encodages bit-décimaux, on n'a pas ce problème, 0,2 étant codé 0000 , 0010.

Il a existé des ordinateurs qui travaillaient uniquement avec de tels encodages, appelés des ordinateurs décimaux. Ils étaient assez courants entre les années 60 et 70, même s'ils ne représentent pas la majorité des architectures de l'époque. Avec eux, la mémoire n'était pas organisée en octets, mais elle stockait des chiffres décimaux codés sur 5-6 bits. Le processeur faisait des calculs sur des chiffres bit-décimaux directement.

Leur grand avantage est leur très bonne performance dans les taches de bureautique, de comptabilité, et autres. Les processeurs de l'époque recevaient des entiers codés en bit-décimal de la part des entrées-sorties, et devaient les traiter. Les processeurs binaires devaient faire des conversions décimal-binaire pour communiquer avec les entrées-sorties, mais pas les processeurs décimaux. Le gain en performance pouvait être substantiel dans certaines applications.

Les ordinateurs décimaux se classent en deux sous-types bien précis. Les premiers gèrent des entiers qui ont un nombre de chiffres fixes. Par exemple, l'IBM 7070 gérait des entiers de 10 chiffres, plus un signe +/- pour les entiers signés. Le processeur faisait les calculs directement en bit-décimal, il gérait des entiers faisant environ 10-15 chiffres décimaux et savait faire des calculs avec de tels nombres. Le second sous-type effectue les calculs chiffre par chiffre et géraient des nombres de taille variable, sans limite de chiffres ! Nous reparlerons de ces derniers dans un chapitre ultérieur, quand nous parlerons de la différence entre byte et mot. Pour le moment, ne gardez à l'esprit que les processeurs gérant un nombre fixe de chiffres décimaux, plus simples à comprendre.

Le Binary Coded Decimal

[modifier | modifier le wikicode]

Le Binary Coded Decimal, abrévié BCD, est une représentation qui mixe binaire et décimal. Avec cette représentation, les nombres sont écrits en décimal, comme nous en avons l'habitude dans la vie courante, sauf que chaque chiffre décimal est directement traduit en binaire sur 4 bits. Prenons l'exemple du nombre 624 : le 6, le 2 et le 4 sont codés en binaire séparément, ce qui donne 0110 0010 0100.

Codage BCD
Nombre encodé (décimal) BCD
0 0 0 0 0
1 0 0 0 1
2 0 0 1 0
3 0 0 1 1
4 0 1 0 0
5 0 1 0 1
6 0 1 1 0
7 0 1 1 1
8 1 0 0 0
9 1 0 0 1

On peut remarquer que 4 bits permettent de coder 16 valeurs, là où il n'y a que 10 chiffres. Dans le BCD proprement dit, les combinaisons de bits qui correspondent à 10, 11, 12, 13, 14 ou 15 ne sont tout simplement pas prises en compte. Sur quelques ordinateurs, ces combinaisons codent des chiffres décimaux en double : certains chiffres pouvaient être codés de deux manières différentes. Il est aussi possible d'utiliser ces valeurs pour coder quelque chose d'autre que des chiffres. Par exemple, il est possible de les utiliser pour coder un signe + ou -, afin de gérer les entiers relatifs. Une autre possibilité, complémentaire de la précédente, utilise ces valeurs en trop pour coder une virgule, afin de gérer les nombres non-entiers. Les possibilités sont nombreuses.

Le support du BCD implique souvent que le processeur supporte des opérations BCD, à savoir des opérations capables de travailler sur des opérandes en BCD et de donner un résultat en BCD. Il faut bien faire la différence entre les opérations en binaire et les opérations en BCD. Par exemple, on n'effectue pas une addition de la même manière en binaire et en décimal/BCD, même si les grandes lignes sont presque identiques. Les différences font que les processeurs doivent avoir des opérations différentes pour les deux encodages, de la même manière que les processeurs gèrent les opérations sur les flottants et les entiers séparément.

Le support du codage BCD est abandonné de nos jours, c'est surtout quelque chose qu'on trouve sur les anciens processeurs. Les architectures 8 et 16 bits supportaient à la fois des opérations binaires et des opérations BCD. Mais il a aussi existé une méthode intermédiaire, qui utilisait des additions binaires normales sur des opérandes BCD. L'idée est que le résultat de l'addition est incorrect, car les valeurs 10 à 15 peuvent apparaître comme chiffre dans le résultat. Mais on peut le corriger pour obtenir le résultat exact en BCD. Pour résumé, les processeurs faisaient des additions en binaire, et corrigeaient le résultat avec une opération spécifique pour obtenir un résultat en BCD. Nous en reparlerons dans le chapitre sur le langage machine et l'assembleur, dans lequel nous étudierons les différentes opérations que supporte un processeur.

Les encodages compacts du BCD

[modifier | modifier le wikicode]

Des variantes du BCD visent à réduire le nombre de bits utilisés pour encoder un nombre décimal. Nous allons les appeler les encodages BCD compacts. Avec certaines variantes, on peut utiliser seulement 7 bits pour coder deux chiffres décimaux, au lieu de 8 bits en BCD normal. Idem pour les nombres à 3 chiffres décimaux, qui prennent 10 bits au lieu de 12. Cette économie est réalisée par une variante du BCD assez compliquée, appelée l'encodage Chen-Ho. Une alternative, appelée le Densely Packed Decimal, arrive à une compression identique, mais avec quelques avantages au niveau de l'encodage. Ces encodages sont cependant assez compliqués à expliquer, surtout à ce niveau du cours, aussi je me contente de simplement mentionner leur existence.

Les encodages BCD compacts sont utilisés par les programmeurs pour stocker des données dans un fichier, en mémoire, mais guère plus. Ils ne sont pas gérés par le processeur directement, on ne peut pas faire de calculs avec, le processeur ne gére pas d'opération BCD supportant de tels encodages. C'est théoriquement possible, mais ça n'a jamais été implémenté dans un processeur, le cout en circuit n'en valait pas la chandelle. Pour faire des calculs sur des nombres en BCD compact, on doit les décompresser et les convertir en BCD normal ou en binaire, puis faire les calculs avec des opérations BCD/binaire usuelles.

L'encodage Excess-3

[modifier | modifier le wikicode]

La représentation Excess-3 (XS-3) est une variante du BCD, qui a autrefois était utilisée sur d'anciens ordinateurs décimaux. Il s'agit d'une sorte d'hybride entre une représentation par excès et BCD. Chaque chiffre décimal est codé sur plusieurs bits en utilisant une représentation binaire par excès. Le biais est de 3 pour la représentation XS-3, il en existe des variantes avec un excès de 4, 5, mais elles sont moins utilisées. Avec elle, la conversion d'un chiffre décimal se fait comme suit : on prend le chiffre décimal, on ajoute 3, puis on traduit en binaire.

Codage en Excess-3
Décimal Binaire
-3 0 0 0 0
-2 0 0 0 1
-1 0 0 1 0
0 0 0 1 1
1 0 1 0 0
2 0 1 0 1
3 0 1 1 0
4 0 1 1 1
5 1 0 0 0
6 1 0 0 1
7 1 0 1 0
8 1 0 1 1
9 1 1 0 0
10 1 1 0 1
11 1 1 1 0
12 1 1 1 1

L'avantage de cette représentation est que l'on peut facilement calculer les soustractions en utilisant une méthode bien précise (celle du complément à 10, je ne détaille pas plus). Le défaut est que le calcul des additions est légèrement plus complexe.

L'XS-3 a été utilisé sur quelques ordinateurs décimaux assez anciens, notamment sur l'UNIVAC I et II.

Le code 2 parmi 5

[modifier | modifier le wikicode]

Les codes 2 parmi 5 permettent d'encoder 10 chiffres décimaux sur 5 bits. Le code garantit que sur les 5 bits, deux sont à 1, les trois restants sont à 0, les bits à 0/1 n'étant pas les mêmes selon le chiffre encodé et le code utilisé. Il existe plusieurs codes 2 parmi 5, la plupart sont utilisés sur les codes barres dans les magasins, mais ce ne sont pas ceux utilisés dans les ordinateurs d'antan.

L'ordinateur IBM 7070 et ses déclinaisons utilisait un code 2 parmi 5 qui codait les 10 chiffres décimaux, ainsi que les signes + et -, et un caractère . Il gérait des entiers décimaux codés sur 10 chiffres plus un caractère +/- pour le signe. Les caractères pour le texte étaient codés en utilisant un code à deux chiffres décimaux, c’est-à-dire sur 10 bits.

0 1 2 3 4 5 6 7 8 9 A - +
01100 11000 10100 10010 01010 00110 10001 01001 00101 00011 1––10 1––01 0––11

Les codes bi-quinaires

[modifier | modifier le wikicode]

Les codes bi-quinaires codent des chiffres sur 7 bits. Les 7 bits sont découpés en deux sections : 2 bits pour la première, 5 pour l'autre. Les 5 bits permettent de compter de 0 à 4 ou de 5 à 9, un seul bit est à 1, les quatre autres sont à 0. Le bit mis à 1 indique la valeur encodée. Les deux bits restants déterminent si la valeur est inférieure ou supérieure/égale à 5, un seul des deux bits est à 1. Voici les deux encodages les plus courants :

Code bi-quinaire basique
Code bi-quinaire réfléchi.

L'originalité de ce codage est qu'il permet de facilement compter sur ses doigts : on a deux mains, cinq doigts. De nombreuses langues utilisent ce système pour coder des nombres, comme le Wolof. Et ce système est utilisé dans certains pays pour compter sur ses doigts, voire dans la vie de tous les jours. Et il a été utilisé sur d'anciens ordinateurs, dont l'IBM 650, l'UNIVAC Solid State et le UNIVAC LARC.

Pour rentrer vraiment dans le détail, voici les encodages utilisés sur ces machines. La lecture est facultative, il est possible de passer directement à la section suivante :

IBM 650 Remington Rand 409 UNIVAC Solid State UNIVAC LARC
Chiffre 1357-9 bits 05-01234 p-5-421 bits p-5-qqq bits
0 10-10000 0000-0 1-0-000 1-0-000
1 10-01000 1000-0 0-0-001 0-0-001
2 10-00100 1000-1 0-0-010 1-0-011
3 10-00010 0100-0 1-0-011 0-0-111
4 10-00001 0100-1 0-0-100 1-0-110
5 01-10000 0010-0 0-1-000 0-1-000
6 01-01000 0010-1 1-1-001 1-1-001
7 01-00100 0001-0 1-1-010 0-1-011
8 01-00010 0001-1 0-1-011 1-1-111
9 01-00001 0000-1 1-1-100 0-1-110

Les encodages binaires alternatifs

[modifier | modifier le wikicode]

Outre le binaire et le BCD que nous venons de voir, il existe d'autres manières de coder des nombres en binaires. Et nous allons les aborder dans cette section. Parmi celle-ci, nous parlerons du code Gray et de la représentation one hot. Nous en parlons car elles seront utiles dans la suite du cours, bien que de manière assez limitée. Autant nous passerons notre temps à parler du binaire normal, autant les représentations que nous allons voir sont aujourd'hui utilisées dans des cas assez spécifiques. Et elles sont plus courantes que vous ne le pensez.

Le code Gray est un encodage binaire qui a une particularité très intéressante : deux nombres consécutifs n'ont qu'un seul bit de différence. Pour exemple, voici ce que donne le codage des 8 premiers entiers sur 3 bits :

Décimal Binaire naturel Codage Gray
0 000 000
1 001 001
2 010 011
3 011 010
4 100 110
5 101 111
6 110 101
7 111 100

Les utilisations du code Gray sont assez nombreuses, bien qu'on n'en croise pas tous les jours. Un exemple : le code Gray est très utile dans certains circuits appelés les compteurs, qui mémorisent un nombre et l'incrémentent (+1) ou le décrémentent (-1) suivant les besoins de l'utilisateur. Il est aussi utilisé dans des scénarios difficiles à expliquer ici (des codes correcteurs d'erreur ou des histoires de passages de domaines d'horloge). Mais le point important est que ce code sera absolument nécessaire dans quelques chapitres, quand nous parlerons des tables de Karnaugh, un concept important pour la conception de circuits électroniques. Ne passez pas à côté de cette section.

Pour construire ce code Gray, on peut procéder en suivant plusieurs méthodes, les deux plus connues étant la méthode du miroir et la méthode de l'inversion.

Construction d'un code gray par la méthode du miroir.

La méthode du miroir est relativement simple. Pour connaître le code Gray des nombres codés sur n bits, il faut :

  • partir du code Gray sur n-1 bits ;
  • symétriser verticalement les nombres déjà obtenus (comme une réflexion dans un miroir) ;
  • rajouter un 0 au début des anciens nombres, et un 1 au début des nouveaux nombres.

Il suffit de connaître le code Gray sur 1 bit pour appliquer la méthode : 0 est codé par le bit 0 et 1 par le bit 1.

Une autre méthode pour construire la suite des nombres en code Gray sur n bits est la méthode de l'inversion. Celle-ci permet de connaître le codage du nombre n à partir du codage du nombre n-1, comme la méthode du dessus. On part du nombre 0, systématiquement codé avec uniquement des zéros. Par la suite, on décide quel est le bit à inverser pour obtenir le nombre suivant, avec la règle suivante :

  • si le nombre de 1 est pair, il faut inverser le dernier chiffre.
  • si le nombre de 1 est impair, il faut localiser le 1 le plus à droite et inverser le chiffre situé à sa gauche.

Pour vous entraîner, essayez par vous-même avec 2, 3, voire 5.

La représentation one-hot

[modifier | modifier le wikicode]

Dans le représentation one-hot, les nombres sont codés de manière à ce que un seul bit est à 1 pendant que les autres sont à 0. Cela laisse peu de valeurs possibles : pour N bits, on peut encoder seulement N valeurs. Les entiers sont codés de la manière suivante : le nombre N est encodé en mettant le énième bit à 1, avec la condition que l'on commence à compteur à partir de zéro. Dit autrement, le nombre encodé est égal au poids du bit à 1. Par exemple, si le bit de poids faible (celui de poids 0) est à 1, alors on code la valeur 0. Si le bit de poids numéro 1 est à 1, alors on code la valeur 1. Et ainsi de suite.

Décimal Binaire One-hot
0 000 00000001
1 001 00000010
2 010 00000100
3 011 00001000
4 100 00010000
5 101 00100000
6 110 01000000
7 111 10000000
Il est important de remarquer que dans cette représentation, le zéro est n'est PAS codé en mettant tous les bits à 0. Le zéro est codé en mettant le bit de poids faible à 1.

L'utilité de cette représentation n'est pas évidente. Mais sachez qu'elle le deviendra quand nous parlerons des circuits appelés les "compteurs", tout comme ce sera le cas pour le code Gray. Elle est très utilisée dans des circuits appelés des machines à état, qui doivent incorporer des circuits compteurs efficients. Et cette représentation permet d'avoir des circuits pour compter qui sont très simples, efficaces, rapides et économes en circuits électroniques.

Les encodages ternaires

[modifier | modifier le wikicode]

Après avoir vu le binaire, il est temps de finir en beauté avec le ternaire ! Le ternaire n'a en soi rien à voir avec le binaire, c'est un encodage au même titre que le binaire, le décimal, l'hexadécimal, ou autre. Il encode un nombre non pas en base 10 comme le décimal, en base 2 comme le binaire, ou en base 16 comme l'hexadécimal, mais en base 3 ! Il encode un nombre en utilisant non pas des chiffres, ni des bits, mais des trits qui peuvent prendre trois valeurs.

Dans le ternaire normal, les trois valeurs d'un trit sont 0, 1 et 2. L'encodage multiple un trit par une puissance de 3, à savoir 1, 3, 9, 27, etc. On parle alors de ternaire non-équilibré. Inutile de développer plus, le principe est le même qu'en binaire ou en décimal, mais en base 3. De plus, un tel encodage n'est pas très utilisé en informatique, aucun ordinateur ne l’utilise, contrairement aux suivants.

Le ternaire non-équilibré est une variante du ternaire normal, où chaque trit encode les trois valeurs suivantes : 0, 1 et -1. Le trit est donc signé ! L'équivalent en binaire serait d'encoder les trois valeurs sur deux bits : un bit de signe et un bit pour coder 0/1. La différence est qu'utiliser deux bits fait qu'on a un zéro positif et un zéro négatif.

Valeurs encodables par un trit
Ternaire non-équilibré 0 1 2
Ternaire équilibré −1 0 1

Le ternaire non-équilibré a été utilisé sur d'anciens ordinateurs ternaires, dont le plus connu est l'ordinateur ETUN de l'université de Moscou. Il s'agissait de véritables ordinateurs ternaires, qui utilisaient des mémoires et des processeurs ternaires, qui géraient des trits directement. Il ne s'agit pas d'équivalents aux ordinateurs décimaux, qui encodaient des chiffres décimaux en binaire, qui étaient des hybrides entre ordinateurs décimaux et binaire, avec une interface décimale, mais une conception fondamentalement binaire. Les ordinateurs ternaires sont fondamentalement ternaires, il n'y a de bit nulle part dans de tels ordinateurs, seulement des trits !

Les ordinateurs ternaires utilisaient tous le ternaire équilibré. L'avantage de cet encodage est qu'il permet de représenter des entiers positifs comme négatifs, sans compter que certaines opérations sont bien plus simples à implémenter qu'en binaire. Par exemple, calculs l'opposé d'un nombre, son complément, est très simple : il suffit d'inverser tous les trits du nombre : les trits à 1 sont remplacés par des -1, et inversement, les zéros sont laissés tels quels. La soustraction demande de calculer le complément de l'opérande à soustraire, et d'additionner, encore plus simple qu'en complément à deux. L'addition et la multiplication sont plus simples, car la gestion des retenues et des tables de multiplication est légèrement plus simple.