Les équations sur ce site sont affichées avec MathJax.
Si vous êtes curieuse/ curieux, je vous suggère d'examiner le site http://www.infoverse.org/octomatics/octomatics.htm qui propose un système de numération octale un peu particulier, avec une notation un peu hors convention pour l'écriture de chaque chiffre.
En informatique, nous faisons toujours face à des ressources limitées. Un monde idéal où il y a assez d'espace pour écrire un nombre fait d'une infinité de chiffres ne nous appartient pas, sauf en imagination.
Parce que nous sommes contraints à des ressources limitées, nous nous dotons de façons de faire qui en rendent l'utilisation efficace. Dans la présente, nous examinerons brièvement la structure interne des nombres dans un ordinateur, mettant principalement l'accent sur les nombres entiers.
« Les nombres, surtout les entiers, on connaît ça ». Nous sommes tellement habitués à une notation décimale qu'on ne fait plus attention, à un moment donné, à ce que l'on représente vraiment par leur écriture.
Prenons par exemple l'entier . Nous savons très bien ce que ce nombre signifie : il signifie cent vingt-trois. Et pourtant, il y a d'autres moyens de l'examiner.
Une de ces manières d'examiner ce nombre est plus pertinente à nos fins que d'autres. On peut en effet réécrire au sens conventionnel de la manière suivante :
Cette forme tient parce que notre système usuel de numérotation est à base décimale (on dit aussi en base ). On compose des nombres en disposant des chiffres décimaux de manière à ce que la position de chaque chiffre ait un sens en soi.
Chaque chiffre peut prendre valeurs distinctes (de à inclusivement), d'où les vocables « en base » et « en notation décimale ». On obtient la valeur d'un nombre en base en multipliant chaque chiffre par élevé à la puissance indiquée par sa position, puis en additionnant les valeurs ainsi obtenues.
Exemple : prenons le nombre décimal . Il peut aussi s'écrire comme suit :
donc :
et par conséquent :
Le chiffre le plus à droite est par convention le moins significatif, et est multiplié par la base () élevée à la puissance . Le second est multiplié par la base, cette fois élevée à la puissance ; le prochain est multiplié par la base élevée à la puissance ; etc.
D'autres exemples :
Le nombre décimal... | ...signifie en fait |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
On utilise une base par habitude. La plupart des humains ont doigts, orteils, et il y a un lien entre le choix d'un système à chiffres et cette réalité.
Mais on pourrait très bien utiliser une autre base que la base , et la mécanique demeurerait la même. L'emploi de la base n'est rien d'autre qu'une convention, une question d'habitude. Ce n'est pas la seule méthode, la seule base possible.
Le choix d'une base indique le nombre de chiffres auxquels on a accès pour écrire un nombre. Quand on utilise la base , on a accès à chiffres distincts (de à inclusivement) pour écrire nos nombres. Si on utilise la base , on aura accès à chiffres (de à inclusivement).
Pour indiquer la base dans laquelle un nombre est rédigé, on indique celle-ci en indice (en bas à droite du nombre en question, écrit en plus petit). Par exemple, signifie « en base ». En mathématiques, la plupart du temps, un nombre pour lequel on n'indique pas de base est un nombre décimal.
On pourrait utiliser la base , par exemple (les chiffres iraient alors de à inclusivement). Dans ce cas, on pourrait écrire le nombre suivant (le petit à la fin indique l'emploi de la base ) :
en notation décimale, cela nous donnerait (pris de droite à gauche, alors attention!) :
ce qui équivaut à :
donc :
...ce qui équivaut à :
Ainsi, le nombre ( en base ) représente la même valeur que le nombre ( en base ). Ce sont là deux manières différentes d'écrire la même idée.
Par habitude, nous – les humains – pensons nos nombres en base . C'est donc le mode par défaut pour interagir avec des humains de plusieurs langages de programmation comme C++.
Le langage C++ utilise par défaut la notation décimale. Pas pour aider l'ordinateur, bien sûr, mais plutôt pour faciliter la vie des humains.
Lorsqu'on compile un programme C++, les opérations que le compilateur y trouve sont traduites en langage machine, et les nombres y sont représentés de manière à faciliter la tâche de l'ordinateur.
Pour un ordinateur, la base ne serait pas pratique, entre autres parce qu'il faudrait trouver une manière de codifier chiffres distincts pour implanter des systèmes de numérotation.
Les ordinateurs, si on regarde très près de la machine (à très bas niveau), ne comprennent que la base – où l'on ne trouve que deux chiffres possibles, et .
Les nombres écrits en base sont nommés nombres binaires.
Deux autres bases sont assez communes dans le monde merveilleux de l'informatique, en particulier avec le langage C++. Ce sont les bases (on parle de nombres en notation octale) et (nombres en notation hexadécimale).
Nous allons les expliquer brièvement, et montrer comment les exprimer en C++. La notation de C++ est la même que pour C, Java, C# et plusieurs autres langage.
Charles Nicholson, dans http://corner.squareup.com/2013/07/reversing-bits-on-arm.html (texte de 2013) propose des algorithmes pour inverser l'ordre des bits dans un entier.
L'unité minimale de donnée dans un ordinateur est le bit. Un bit est un chiffre binaire (base ), dont la valeur ne peut donc être que ou , et rien d'autre.
Si on ne prend qu'un seul bit, on ne peut exprimer que deux valeurs distinctes ( et ). Si on prend deux bits, on peut exprimer quatre () valeurs distinctes :
avec trois bits, on pourra exprimer huit () valeurs distinctes :
avec quatre bits, on pourra exprimer seize () valeurs distinctes :
et, de manière générale, avec bits, on pourra alors exprimer valeurs distinctes. Ainsi, avec bits, on pourra exprimer (donc ) valeurs distinctes – que nous n'énumérerons pas.
Puisqu'on considérera qu'un nombre binaire fait entièrement de zéros vaut zéro, on dira que le plus grand nombre possible sur bits est . Ainsi, sur bits, les nombres iront de à (de à ), ce qui donne bien possibilités.
Exercice : étant donné ce que nous avons vu sur les nombres et les bases, plus haut, exprimez sous forme décimale les nombres binaires ci-dessous :
Notation binaire | Équivalent en notation décimale |
---|---|
donc |
En mathématiques, les nombres sont souvent idéalisés, pouvant être de précision infinie (pensons à par exemple, qu'on approche de manière aussi précise qu'on le veut).
En informatique, la mémoire disponible est nécessairement limitée, donc les nombres à développement infini ne sont pas envisageables. L'informaticienne et l'informaticien doivent vivre avec ce genre de considération.
De même, les besoins de vitesse font en sorte qu'on travaille presque toujours sur des regroupements d'un certain nombre de bits à la fois – ce nombre est d'ailleurs pratiquement toujours une puissance de deux.
Le mot anglais byte et le mot français octet sont presque synonymes, mais il y a eu historiquement des bytes de six ou de dix bits. Un byte anglais est la plus petite unité directement adressable dans un ordinateur donné. Aujourd'hui, byte équivaut, dans l'esprit de la majorité, à octet.
Le regroupement le plus petit auquel on ait souvent affaire est l'octet (huit bits). Le charme de l'octet est qu'il permet de représenter () valeurs distinctes, ce qui permet – entre autres – de couvrir l'étendue des touches du clavier américain.
On trouve aussi de manière commune des regroupements de ou de bits (parfois même de ou de bits sur certains ordinateurs haut de gamme), donc de ou octets. Il est courant qu'on pense la taille d'un nombre en terme du nombre d'octets qui sont utilisés pour l'encoder.
Pour se donner une idée de ce que ces tailles signifient, examinons un peu :
Avec... | ...donc... | ...on a... |
---|---|---|
bits | octet | () possibilités |
bits | octets | () possibilités |
bits | octets | () possibilités |
bits | octets | () possibilités |
Pour la plupart des calculs, encore aujourd'hui, des entiers représentés sur bits suffisent amplement. Les ordinateurs à votre disposition sont généralement bien équipés pour gérer des calculs sur des nombres encodés sur bits, et de plus en plus d'ordinateurs personnels représentent de manière native des entiers sur bits.
Binaire | Décimal |
---|---|
Comment représente-t-on des entiers dans un ordinateur? La première question à se poser est toujours des entiers de quelle taille? (sous-entendu : encodés sur combien de bits?). La seconde est : parle-t-on d'entiers signés ou non signés?
Un entier non signé encodé sur bits peut prendre des valeurs allant de à (ce qui couvre une plage de valeurs distinctes). Concrètement, un entier non signé sur huit bits peut représenter des valeurs allant de à , donc de à inclusivement.
Si on regarde la valeur décimale associée aux différents encodages binaires, on verra alors ce que voulez voir à droite.
On voit qu'un encodage de vaut précisément zéro; que vaut effectivement ; que équivaut à (en base ); et ainsi de suite, ce qui respecte les règles décrites dans la section sur les bases, plus haut.
Naturellement, vaut (faites vous-mêmes le calcul pour vous en convaincre : ). Puis, vaut , vaut , etc. jusqu'à qui vaut .
Exercice : les entiers binaires suivants sont des entiers non signés sur bits. Exprimez chacun sous forme décimale.
Sous forme binaire | Sous forme décimale |
---|---|
Exercice : les entiers décimaux suivants sont des entiers non signés sur bits. Exprimez chacun sous forme binaire.
Sous forme décimale | Sous forme binaire |
---|---|
Binaire | Décimal |
---|---|
Un entier signé encodé sur bits peut prendre des valeurs allant de à (ce qui couvre encore une fois une plage de valeurs distinctes).
Concrètement, un entier non signé sur huit bits peut représenter des valeurs allant de à , donc de à inclusivement.
On a choisi d'encoder les valeurs ainsi pour permettre une étendue qui aurait le même nombre de valeurs positives que négatives (considérant, par convention, comme positif).
Si on regarde la valeur décimale associée aux différents encodages binaires, on verra alors (à droite) :
Dans le cas des entiers signés, relevons que le bit le plus à gauche de la représentation choisie est à lorsque le nombre est négatif, et qu'il est à lorsque le nombre est positif. On nomme ce bit bit de signe.
Ce qui diffère donc, au fond, entre un entier non signé et un entier signé, c'est l'interprétation faite d'un seul bit. Aussi, utiliser un bit pour dénoter le signe fait en sorte qu'il y ait exactement la même quantité de nombres positifs et négatifs, peu importe le nombre de bits employés dans la représentation.
Exercice : les entiers binaires suivants sont des entiers signés sur bits. Exprimez chacun sous forme décimale.
Sous forme binaire | Sous forme décimale |
---|---|
Exercice : les entiers décimaux suivants sont des entiers signés sur bits. Exprimez chacun sous forme binaire.
Sous forme décimale | Sous forme binaire |
---|---|
Si cet entier valait... | ...il vaudra maintenant... |
---|---|
? |
Si les nombres que nous manipulons ont une taille finie (plutôt qu'infinie), il est possible qu'on provoque, en opérant sur eux, ce qu'on appelle des débordements.
Prenons par exemple un entier non signé sur bits (valeurs allant donc de à inclusivement), et ajoutons-lui .
Si notre entier valait , il vaudra maintenant ... au fait, que vaudra-t-il? Sûrement pas , puisqu'il est impossible d'encoder cette valeur si notre échelle va de à inclusivement.
En fait, il vaudra maintenant , et nous aurons causé un débordement. Le terme débordement tient du fait que la capacité de représentation de notre type de données (entier sur bits non signé) aura été excédée par notre opération.
Un débordement se représente simplement avec une addition, mais peut être causé par toute opération dont le résultat excède les bornes possibles du type de données choisi.
L'idée de débordement peut être représentée à l'aide d'un cercle, autour duquel on trouve les valeurs possibles pour la représentation choisie (ici, on utilise encore les entiers signés sur huit bits, question de garder l'exemple le plus simple possible). L'addition peut être vue comme tourner dans le sens horaire. Vu ainsi, donne effectivement .
Souvenez-vous de l'opérateur modulo et de ses effets. Voyez-vous un lien avec cette image?
Le même phénomène cyclique se produit sur des entiers signés, à la différence près que le débordement ne se produit pas aux mêmes valeurs.
Ici, la frontière entre le plus grand nombre et le plus petit nombre pouvant être représentés est franchie quand on passe de à et inversement. En effet, sur des entiers signés encodés sur huit bits, et .
Les exemples donnés ci-dessus l'ont été à l'aide d'entiers sur bits, mais les idées associées valent pour des entiers de n'importe quelle taille ( bits, bits, ...), dans la mesure où celle-ci est finie.
Sur bits, par exemple, la frontière se situe entre (donc ) et pour les entiers non signés... | ...et entre () et (donc ) pour les entiers signés. |
![]() |
![]() |
Le langage C++ offre un certain nombre de types de données pour représenter des entiers. Ainsi, on a accès aux types suivants :
Type | Taille typique[1] |
---|---|
bool |
Habituellement encodé sur huit bits. Un bool peut prendre les valeurs vrai (true) ou faux (false). On reviendra sur son cas sous peu. Notez qu'on ne s'en servira habituellement pas pour représenter des nombres |
char |
Habituellement encodé sur huit bits. On s'en sert entre autres pour représenter des caractères, mais le type char a pour rôle principal de représenter un byte – il s'agit d'un type très, très important |
wchar_t |
Encodé sur 16 ou 32 bits selon les plateformes. On s'en sert surtout pour représenter des caractères étendus (ce qui déborde de l'alphabet américain) |
short |
Habituellement encodé sur 16 bits |
int |
Habituellement encodé sur 32 bits |
long |
Habituellement encodé sur 32 bits |
long long |
Habituellement encodé sur 64 bits |
Par défaut, ces types sont signés. On peut spécifier le caractère signé en préfixant le type du mot signed. On peut aussi spécifier un entier comme étant non signé par le préfixe unsigned.
Exemples :
// Quelques valeurs approximatives
unsigned int nb_humains;
// approx. 8000000
unsigned short population_CLG; // approx. 5850
// Pour celle ci-dessous, on aurait pu ecrire « short » simplement
signed short fluctuation_compte_bancaire; // c'est un secret!
Exercice : avec ce que l'on sait maintenant, qu'affichera le programme suivant?
#include <iostream>
int main() {
using namespace std;
short s;
s = 32767;
cout << s << endl;
s = s + 1;
cout << s << endl;
}
Depuis C++ 11, il est possible de définir nos propres littéraux, mais je n'ai pas d'exemple à vous proposer pour le moment, faute de temps. Ça viendra...
En C++, on peut représenter un littéral entier sous trois formes différentes :
Il n'y a pas, à même le langage, de notation binaire. Toutefois, nous le verrons plus bas, il est très simple de passer de la notation hexadécimale à la notation binaire, et inversement.
La notation décimale est celle, commune, que nous utilisons habituellement sans trop nous poser de questions. Les littéraux , et sont des littéraux décimaux (base ), et ont le sens usuel.
La notation octale débute l'écriture d'un nombre par le chiffre . Ainsi, , et sont tous des nombres écrits en notation octale (leurs équivalents décimaux sont, respectivement, , et ).
Le chiffre a le même sens en notation octale comme en notation décimale.
Puisque la notation octale (base ) n'utilise que des chiffres allant de à inclusivement, l'écriture est illégale ( n'est pas un chiffre valide en notation octale). En base , des nombres comme ou sont illégaux parce qu'ils sont en partie faits de chiffres qui n'y existent pas.
Exercices : Vous trouverez ci-dessous des nombres exprimés sous forme octale, de la manière comprise par le compilateur C++. Exprimez chacun d'entre eux sous forme décimale (non signée) et sous forme binaire[2].
Forme octale | Forme décimale | Forme binaire |
---|---|---|
La notation octale existe en C++ surtout pour des raisons historiques. Le langage C++ s'est inspiré, côté syntaxe, du langage C, très massivement utilisé dans l'industrie, à un tel point qu'en théorie, un compilateur C++ devrait être capable de compiler presque tout programme écrit en langage C.
Le langage C, lui, supportait la norme octale surtout parce qu'il a été développé, à l'origine, sur des ordinateurs qui, eux, utilisaient un format octal pour représenter les opérations très près de la machine.
Ça a un côté pratique: chaque chiffre octal peut prendre huit, donc , valeurs différentes. Pour un nombre comme , on peut donc arriver facilement au nombre binaire correspondant, chaque chiffre octal correspondant précisément à trois bits.
Ainsi :
|
||||
---|---|---|---|---|
Vaut (sous forme binaire ):
|
Pour les gens qui programmaient à cette époque, l'aisance avec laquelle on pouvait passer d'un nombre écrit sous forme octale à un nombre écrit sous forme binaire était d'une grande utilité.
Même si elle est beaucoup moins utilisée qu'auparavant, la notation octale mérite d'être mentionnée au passage... Sinon, expliquer pourquoi n'est pas un nombre deviendrait une tâche fort difficile.
La notation hexadécimale débute l'écriture d'un nombre par . Ainsi, , et sont tous des nombres écrits en notation hexadécimale (leurs équivalents décimaux sont, respectivement, , et ).
Le chiffre a le même sens que le en notation octale ou décimale.
Puisque la notation hexadécimale (base ) nécessite chiffres distincts, on utilise les chiffres allant de à inclusivement de même que les lettres à ( valant en notation décimale, et valant ). Ces « lettres » peuvent s'écrire en majuscules ou en minuscules, à votre convenance (j'utiliserai les minuscules ici).
Ainsi, l'écriture est illégale ( n'est pas un chiffre valide en notation octale), et l'écriture n'est pas un nombre. En revanche, les écritures et sont tout à fait légales.
Exercices : Vous trouverez ci-dessous des nombres exprimés sous forme hexadécimale, de la manière comprise par le compilateur C++. Exprimez chacun d'entre eux sous forme décimale (non signée) et sous forme binaire[3].
Forme hexadécimale | Forme décimale | Forme binaire |
---|---|---|
La notation hexadécimale existe en C++ pour des raisons analogues à celles justifiant l'écriture octale (voir ci-dessus). Contrairement à l'écriture octale, quelque peu désuète, l'écriture hexadécimale est encore fort utilisée.
En effet, chaque chiffre hexadécimal peut prendre seize () valeurs différentes. Pour un nombre comme , on peut donc arriver facilement au nombre binaire correspondant, chaque chiffre hexadécimal correspondant précisément à quatre bits (on nomme parfois nybble un bloc de quatre bits) – conséquemment, un octet est représenté par un nombre hexadécimal de deux chiffres.
Ainsi :
|
||||
---|---|---|---|---|
Vaut (sous forme binaire) :
|
Remarque importante sur les notations
Les notations décimale, octale et hexadécimale offrent trois manières différentes d'écrire les mêmes choses. En effet, qu'on écrive , ou , on aura écrit précisément la même chose aux yeux de l'ordinateur. Dans les trois cas, sur le plan binaire, l'ordinateur « voit » .
Pour un petit site pour mieux comprendre la notation binaire, voir http://www.simplecpu.com/Binary.html
Les réels, tout comme les entiers, sont encodés à l'aide de bits. Il y a par contre plus de standards différents pour procéder à cet encodage. Un d'entre eux (une norme IEEE) est plus répandu que les autres, et nous en dresserons l'idée ici.
Au niveau binaire, on manipule beaucoup plus rarement des réels que des entiers. S'il importe de comprendre l'idée de l'encodage, en maîtriser la forme est moins important – si vous en avez un jour besoin, ce qui est improbable, vous pourrez vous référer à la norme elle-même, qui est disponible dans Internet[4].
La représentation des réels est dite à virgule flottante. On nomme ces nombres des nombres à virgule flottante.
Les nombres à virgule flottante ne sont pas représentés en mémoire de la même façon que les entiers, ce qui comporte son lot d'avantages et de désavantages. Leur représentation interne se compose de trois parties : le signe, l'exposant et la mantisse.
signe |
exposant |
mantisse |
Comme dans le cas des entiers, le signe est représenté par un bit unique ( pour les nombres négatifs, pour les nombres positifs), qui est normalement le bit le plus à gauche dans la représentation du nombre.
On lit ces valeurs comme suit :
Selon le nombre de bits utilisés pour représenter un nombre à virgule flottante, la taille de l'exposant et de la mantisse varieront – pour le signe, un bit est toujours suffisant.
Il n'y a pas de nombres à virgule flottante non signés. Le signe fait partie intégrante de toutes les représentations standard.
L'exposant sera un entier pouvant être positif ou négatif. La mantisse sera une fraction exprimée sous forme décimale, toujours entre et .
Un nombre à virgule flottante permet en général de représenter une approximation d'un nombre réel. Plus sa mantisse et son exposant sont grands, plus la plage de valeurs possibles est grande, et il en va de même pour la précision des valeurs représentées. C'est, à peu de choses près, ce qu'il faut en retenir.
Le langage C++ offre un certain nombre de types de données pour représenter des nombres à virgule flottante. Ainsi, on a accès aux types suivants :
Type | Taille[5] |
---|---|
float |
Nombre à virgule flottante, simple précision (typiquement 32 bits) |
double |
Nombre à virgule flottante, double précision (typiquement 64 bits) |
long double |
Nombre à virgule flottante de plus haute précision encore |
Ces types sont tous signés.
Quand nous programmons, nous devons être conscientes et conscients de ces détails techniques, puisqu'ils ont une influence sur la qualité de nos résultats. Un débordement non désiré est souvent une erreur de logique; une manipulation inadéquate d'un nombre réel peut mener à une variété de problèmes et d'assertions erronées – nous y reviendrons.
Conséquemment, le choix du mauvais type de données peut avoir des conséquences allant de mineures (un résultat incorrect dans un travail pratique dans un cours d'introduction à la programmation) à très graves (une erreur dans le calcul de la trajectoire d'un projectile).
Quelques liens pour enrichir le propos.
[1] La taille indiquée vaut pour plusieurs compilateurs, mais pas nécessairement pour tous les compilateurs. Certaines règles s'appliquent d'une plateforme à l'autre, mais les tailles elles-mêmes peuvent varier en fonction du compilateur ou de l'ordinateur. Voir cet article pour plus de détails.
[2] Indice : il y a un truc dans la section Pourquoi la notation octale? pour faciliter la traduction de la forme octale à la forme binaire.
[3] Indice : il y a un truc dans la section Pourquoi la notation hexadécimale? pour faciliter la traduction de la forme hexadécimale à la forme binaire.
[4] Entre autres : http://docs.sun.com/source/806-3568/ncg_goldberg.html.
[5] Voir remarque pour les entiers, plus haut.