1. Représentation des entiers naturels
1.1. Pincipe
Il faut comprendre qu’un nombre peut s’exprimer de différentes manières selon le contexte et les besoins.
1.1.1. Compter en base 10
C’est notre manière naturelle de compter et de représenter les nombres.
La base 10 s’appelle aussi le décimal.
Le principe du comptage consiste à faire des paquets de 10.
Les chiffres ou digits sont 0, 1, 2, 3, 4, 5, 6, 7, 8, 9.
-
est égal à9+1
\((10^1)\) et il reste1 paquet de 10
.0
-
est égal à99+2
\((10^2)\) et il reste10 paquets de 10
.1
Un nombre décimal peut donc s’écrire sous forme de puissance de \(a_n \times 10^n + a_{n-1} \times 10^{n-1} + \ldots +a_1 \times 10^1 + a_0 \times 10^0\) avec \(a_{i} \in [0,1,2,3,4,5,6,7,8,9]\) |
1.1.2. Compter en base 16
La base 16 s’appelle aussi l’hexadécimal.
C’est la base de comptage souvent utilisée pour manipuler les adresses mémoire d’un ordinateur, les adresses des interfaces réseaux, ou pour manipuler des valeurs de registre de composant électronique.
Le principe du comptage consiste à faire des paquets de 16.
Les chiffres ou digits sont 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F. A vaut 10, B vaut 11, …, F vaut 15.
-
.9+1 = A
-
.A+3 = D
-
est égal àF+1
\((16^1)\) et il reste1 paquet de 16
, donc \(10_{|16}\).0
-
est égal àFF+2
\((16^2)\) et il reste16 paquets de 16
, donc \(101_{|16}\)1
Un nombre hexadécimal peut donc s’écrire sous forme de puissance de \(a_n \times 16^n + a_{n-1} \times 16^{n-1} + \ldots +a_1 \times 16^1 + a_0 \times 16^0\) avec \(a_{i} \in [0,1,2,3,4,5,6,7,8,9,10(A),11(B),12©,13(D),14(E),15(F)]\) |
1.1.3. Travail faire : Addition en hexadécimal
Quel résultat des additions suivantes :
-
\(256_{|16}\) + \(DAA_{|16}\)
-
\(EF86_{|16}\) + \(6C39_{|16}\)
-
\(40AE_{|16}\) + \(236A_{|16}\)
-
\(D83B_{|16}\) + \(68A6_{|16}\)
1.1.4. Compter en base 2
La base 2 s’appelle aussi le binaire. C’est la base de comptage souvent utilisée pour manipuler des valeurs de registre de composant électronique, pour effectuer des calculs de sous-réseaux informatique, dans le cadre du développement de systèmes électroniques.
Le principe du comptage consiste à faire des paquets de 2.
Les chiffres ou digits sont 0, 1. Cette information est appelée bit (compression des mots anglais binary digit)
-
est égal à1+1
\((2^1)\) et il reste1 paquet de 2
, donc \(10_{|2}\).0
-
est égal à11+1
\((2^2)\) et il reste2 paquets de 2
, donc \(100_{|2}\).0
Un nombre binaire peut donc s’écrire sous forme de puissance de \(a_n \times 2^n + a_{n-1} \times 2^{n-1} + \ldots +a_1 \times 2^1 + a_0 \times 2^0\) avec \(a_{i} \in [0,1]\) |
1.1.5. Travail faire : Addition en binaire
Quel résultat des additions suivantes :
-
\(11011101_{|2}\) + \(1101_{|2}\)
-
\(10010_{|2}\) + \(1110_{|2}\)
-
\(111000_{|2}\) + \(100110_{|2}\)
-
\(11111_{|2}\) + \(10101_{|2}\)
1.1.6. Compter en base 8
La base 8 s’appelle aussi l'octale. C’est la base de comptage souvent utilisée pour manipuler les droits dans le système de fichiers Linux.
Le principe du comptage consiste à faire des paquets de 8.
Les chiffres ou digits sont 0, 1, 2, 3, 4, 5, 6, 7.
-
est égal à7+1
\((8^1)\) et il reste1 paquet de 8
, \(10_{|8}\).0
-
est égal à77+2
\((8^2)\) et il reste8 paquets de 8
, donc \(101_{|8}\).1
Un nombre octal peut donc s’écrire sous forme de puissance de \(a_n \times 8^n + a_{n-1} \times 8^{n-1} + \ldots +a_1 \times 8^1 + a_0 \times 8^0\) avec \(a_{i} \in [0,1,2,3,4,5,6,7]\) |
1.1.7. Convertir de la base 2 vers la base 16
En programmation des systèmes numériques, il arrive souvent qu’on paramètre des composants électroniques. Les registres des composants sont le plus souvent composés de 8 ou 16 bits, chaque bit pouvant avoir une signification différente. Afin de connaître la valeur globale à paramétrer dans le registre selon nos besoins, il faut définir les valeurs de chaque bit en s’aidant de la documentation du composant ; on obtient alors une valeur en binaire. Les langages de programmation n’acceptent pas le binaire car l’écriture des nombres dans cette base n’est pas pratique car trop long. L’hexadécimal est en revanche très utilisé car l’écriture des nombres est réduite et le passage au binaire est très facile. Pour cette raison, il faut savoir passer du binaire à l’hexadécimal et inversement. |
Méthode
Soit la valeur binaire : \(10100101_{|2}\) sur 8 bits. Le bit le plus à gauche est appelé le MSB (Most Significant Bit), celui le plus à droite le LSB (Less Significant Bit).
A partir du LSB, faire des groupes de 4 bits (quartets) : \(1010 0101_{|2}\).
Convertir chaque quartet en hexadécimal : \(1010_{|2}\) correspond à \(A_{|16}\) et \(0101_{|2}\) correspond à \(5_{|16}\).
Donc, la valeur hexadécimale correspondant à \(10100101_{|2}\) est \(A5_{|16}\).
1.1.8. Convertir de la base 16 vers la base 2
On fait tout simplement l’inverse :
Méthode
On convertie en binaire sur 4 bits chaque digit hexadécimal.
\(F35A_{|16} \Leftrightarrow 1111 \quad 0011 \quad 0101 \quad 1010_{|2}\)
1.1.9. Convertir de la base 8 vers la base 2
On procède comme pour passer de l’hexadécimal au binaire mais on fait des paquets de 3 bits:
Méthode
On convertie en binaire sur 3 bits chaque digit octal.
\(7251_{|8} \Leftrightarrow 111 \quad 010 \quad 101 \quad 001_{|2}\)
1.1.10. Convertir de la base 10 vers la base 2
Nous nous exprimons souvent en décimal car c’est le plus naturel pour nous.
Selon les travaux à réaliser nous aurons besoin de la correspondance en base 2.
Lorsqu’on veut découper un réseau ayant pour masque 255.255.128.0, on doit modifier ce masque en ajoutant des bits à 1 selon le nombre de sous réseaux désirés. Pour découper 2 sous-réseaux, il faut ajouter 1 bit à 1, pour le découper en 8 sous-réseaux, il faut ajouter 3 bits à 1 \((8 = 2^3)\). Pour y arriver de manière fiable, il faut convertir le masque en binaire.
Méthode
On effectue la division entière du nombre à convertir jusqu’à ce que le dividande soit égale à \(0\) ou \(1\). Les restes des divisions correspondent à la représentation binaire du nombre :
Convertir le nombre \(77\) en binaire
Donc, la représentation binaire de \(77_{|10}\) est \(1001101_{|2}\).
Remarque
Un nombre binaire peut s’écrire sous forme de puissance de
de la manière suivante :2
\(a_n \times 2^n + a_{n-1} \times 2^{n-1} + \ldots +a_1 \times 2^1 + a_0 \times 2^0\) avec \(a_{i} \in [0,1]\)
Ils sont souvent représentés sous la forme d’un octet, c’est à dire un paquet de 8 bits. Dans ce cas, on peut écrire le nombre sous forme de puissance de
de la manière suivante :2
\(a_7 \times 2^7 + a_6 \times 2^6 + a_5 \times 2^5 + a_4 \times 2^4 + a_3 \times 2^3 + a_2 \times 2^2 + a_1 \times 2^1 + a_0 \times 2^0\) avec \(a_{i} \in [0,1]\)
Soit :
\(a_7 \times 128 + a_6 \times 64 + a_5 \times 32 + a_4 \times 16 + a_3 \times 8 + a_2 \times 4 + a_1 \times 2 + a_0 \times 1\) avec \(a_{i} \in [0,1]\)
On utilise alors un tableau de conversion.
128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
---|---|---|---|---|---|---|---|
0 |
1 |
0 |
0 |
1 |
1 |
0 |
1 |
Donc, la représentation binaire de \(77_{|10}\) est \(01001101_{|2}\) sur un octet. Le \(0\) de gauche (MSB) est non-significatif et peut donc être omis.
1.1.11. Convertir de la base 2 vers la base 10
On fait tout simplement l’inverse. La méthode la plus simple étant de preprendre le tableau de conversion.
128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
---|---|---|---|---|---|---|---|
1 |
1 |
0 |
1 |
0 |
0 |
0 |
1 |
1.1.12. Travail à faire : Conversions
1.2. Conversions de base en Python
1.2.1. Affichage d’un entier dans une base
En Python (comme pour tout les langages de programmation), il est très facile d’obtenir la représentation d’un entier en base 2, 10 ou 16.
Pour cela, on peut utiliser la méthode format sur les chaînes de caractères. Cette méthode permet de transformer une chaîne de caractères en remplaçant chaque champs de remplacement (sous-chaîne entourée d’accolades {}) par un des arguments de la fonction potentiellement transformé pour correspondre à un format donné.
Par exemple, le code suivant :
"Décimal: {0:d}; Hexadécimal: {0:x}; Binaire: {0:b}".format(42)
permet d’obtenir la représentation de 42 en décimal, hexadécimal et binaire. Le nombre avant les deux points dans les accolades correspond au numéro du paramètre de l’appel de la méthode format alors que la chaîne de caractères après les deux points spécifie le format voulu : d
pour décimal, b
pour binaire et h
pour hexadécimal.
On peut aussi utiliser les fonctions bin
et hex
pour obtenir la représentation d’un entier en binaire ou hexadécimal.
1.2.2. Construire un entier à partir d’une représentation dans une base
Il est aussi possible en Python de créer un entier à partir d’une représentation dans une base quelconque grâce au constructeur int. Un appel int(string_base_representation, base_value)
permet d’obtenir l’entier correspondant à la représentation string_base_representation
en base base_value
.
1.2.3. Travail à faire : Conversions
Ecrire un programme python qui permet d’obtenir :
-
la représentation en base 2 du nombre \(x=FEDCBA9876543210_{|16}\).
-
la représentation en base 16 du nombre \(y=11110000101011110111_{|2}\).
-
Que remarquez-vous entre les deux représentations des nombres \(x\) et \(y\) ?
1.2.4. Travail à faire : Additions
Complétez le programme suivant qui demande à l’utilisateur la base dans laquelle deux nombres sont fournis et calcul leur somme :
base = int(input('base (2, 10, 16) : '))
nb1 = input('nombre 1 : ')
nb2 = input('nombre 2 : ')
somme = ???
resultat = nb1 + ' + ' + nb2 + ' = '
if (base == 2):
resultat = resultat + ???
elif (base == 10):
resultat = resultat + ???
elif (base == 16):
resultat = resultat + ???
print(resultat)
2. Représentation des entiers relatifs en binaire
Un ordinateur manipule des informations stockées en mémoire.
La mémoire est organisée en groupes de 8 bits (octet) repérés par une adresse (souvent exprimée en hexadécimal).
Un octet mémoire permet de mémoriser une valeur comprise entre 0 et 255.
Comment faire pour stocker des valeurs signées négatives ?
Il serait naturel de représenter les entiers relatifs en utilisant un bit pour le signe et les autres pour la valeur absolue. Ainsi, avec des mots de 8 bits, on utiliserait 1 bit pour le signe et 7 bits pour la valeur absolue.
Cependant, il faut que l’ordinateur puisse être capable de réaliser des opérations (addition, …) avec ces valeurs en mémoire.
Additionnons 73 et 48 en base 10 :
\(73_{|10} = 01001001_{|2} \text{ et } 48_{|10} = 00110000_{|2}\)
\(\begin{array}{cc} & 01001001 \\ + & 00110000 \\ \hline & 01111001 \\ \end{array}\)
\(01111001_{|2} = 121_{|10}\)
Les deux nombres étant positifs, tout va bien.
Additionnons maintenant 73 avec -5 : Le résultat attendu est 68.
\(73_{|10} = 01001001_{|2} \text{ et } -5_{|10} = 10000101_{|2}\)
\(\begin{array}{cc} & 01001001 \\ + & 10000101 \\ \hline & 11001101 \\ \end{array}\)
\( 11001101_{|2} = 77_{|10} \) Conclusion le codage du nombre négatif n’est pas le bon.
L’utilisation seule du bit de poid fort pour identifier le signe d’un nombre en binaire n’est pas suffisant. Il faut également coder différemment sa valeur absolue. C’est la méthode dite du "complément à deux qui est utilisée". |
2.1. Complément à deux
La plupart des langages appliquent la méthode appelée "complément à deux".
Le complément à deux ne s’applique qu’à des nombres ayant tous la même longueur :
Ainsi, les entiers naturels de 0 à 32 767 correspondent aux entiers relatifs positifs ou nuls et les entiers naturels de 32 768 à 65 535 représentent les entiers relatifs strictement négatifs. |
Il existe une manière facile de calculer la représentation de l’opposé d’un nombre représenté en complément à deux qui consiste à :
-
Convertir la valeur absolue du nombre
-
Appliquer un complément à 1 : inverser tous les bits (\(1\rightarrow 0\) et \(0\rightarrow 1\))
-
Ajouter 1 au résultat en ignorant le bit supplémentaire si on dépasse le nombre de bits fixé.
-
Conversion de la valeur absolue : \(85_{|10} = 01010101_{|2}\)
-
Complément à 1 : \(10101010_{|2}\)
-
Ajouter 1 : \(10101011_{|2}\)
Donc : \(-85_{|10} = 10101011_{|2}\)
Vérification : \(85 - 85 = 0\) ?
\(\begin{array}{ccc} & 0101 & 0101 \\ + & 1010 & 1011 \\ \hline \color{red}1 & 0000 & 0000 \\ \end{array}\)
En binaire signé, il est très important de connaitre le format des nombres. Ici, nous avions une représentation sur 8 bits, le neuvième bit qui apparait lors de l’opération (retenue), est tout simplement ignoré. |
2.1.1. Travail à faire : Conversions
2.2. Calcul du complément à 2 en python
En python, on peut calculer le complément à deux d’un entier à l’aide de la fonction suivante :
def two_complement_representation(value: int, nb_bits: int):
"""Return the two's complement of value with nb_bits bits"""
assert -(2 ** (nb_bits-1)) <= value < 2 ** (nb_bits-1), \
"Value " + str(value) + " should have been between " + \
str(-(2 ** (nb_bits-1))) + \
" and " + str(2 ** (nb_bits-1)-1)
return ("{0:{fill}" + str(nb_bits) + "b}").format(value % (2 ** nb_bits), fill='0')
2.2.1. Travail à faire : Conversions
-
Calculer (en Python avec la fonction ci-dessus) la représentation en complément à deux sur 16 bits de : -3, -15, -256 et -1024.
-
Vérifier à la main (à l’aide de la méthode facile pour calculer l’opposé) que la fonction donne bien le bon résultat.
-
Comment détecter rapidement à partir de la représentation binaire d’un nombre signé si celui-ci est positif ou négatif ?
-
Additionner en base 2 les nombres correspondant à la représentation en complément à deux de -15 et +16. Quel résultat obtenez-vous ?
-
Pourquoi la fonction
two_complement_representation
calcule bien la représentation en complément à deux devalue
surnb_bits
bits ?