Les opérations bit à bit/Les masques
Dans ce chapitre, nous allons aborder la technique dite des masques. Elle permet de mettre à 0 ou à 1 certains bits dans un nombre. Les bits à modifier peuvent être choisis et il est possible de modifier certains bits sans toucher aux autres. Nous allons d'abord voir les masques proprement dit, mais aussi à quoi cette technique peut servir. Dans ce qui va suivre, nous allons d'abord voir comment modifier un bit dans un nombre, sans toucher aux autres. Nous verrons notamment les champs de bits et quelques autres applications.
Modifier un bit
modifierModifier des bits individuellement dans un nombre demande naturellement d'utiliser des opérations bit à bit. Suivant ce que l'on veut faire, l'instruction utilisée sera différente, mais le principe reste le même. Il suffit d'effectuer une instruction bit à bit entre notre nombre, et une constante bien précise.
Mettre un bit à 1
modifierTout d'abord, nous allons voir comment mettre à 1 un bit dans un nombre. Pour cela, nous allons utiliser une opération bit à bit entre le nombre en question, et un nombre appelé masque qui indique les bits à mettre à 1. Si le bit du masque est à 0, cela signifie que le bit du nombre, situé à la même place, ne doit pas être modifié. Mais si le bit du masque est à 1, le bit du nombre devra être mis à 1. Par exemple, pour modifier le 4éme bit d'un nombre en partant de la droite, le masque est une constante dont le 4éme bit en partant de la droite est 1, et le reste des bits est à 0, soit la constante : 000000001000.
Nous allons devoir trouver quelle opération bit à bit convient. Rien de compliqué au demeurant : il suffit d'écrire la table de vérité de cette opération et de reconnaître la porte logique.
Nombre | Masque | OURésultat |
---|---|---|
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 1 |
On remarque que l’opération bit à bit à effectuer est un OU.
Mettre un bit à 0
modifierMettre un bit à 0 dans un nombre fonctionne sur le même principe que mettre un bit à 1 : on effectue une opération bit à bit entre le nombre en question et un masque qui indique quels bits mettre à 0. Encore une fois, écrire la table de vérité de l'opération nous donne l'opération bit à bit à effectuer. Il s’avère que c'est un ET bit à bit.
Nombre | Masque | ETRésultat |
---|---|---|
0 | 0 | 0 |
0 | 1 | 0 |
1 | 0 | 0 |
1 | 1 | 1 |
On peut se demander à quoi peut bien servir de mettre à 0 certains bits. L'utilité première est d'accéder à un bit particulier dans un nombre, ou à des groupes de bits. Un cas classique est celui de la sélection du bit de signe, pour calibrer certaines opérations arithmétiques. Dans ce cas, on va mettre à zéro les bits inutiles avant d'effectuer un décalage pour caler ces bits à droite. Mais d'autres utilisations plus importantes existent. Par exemple, vous avez peut-être déjà entendu parler des masques de sous-réseau, dans des cours sur le protocole IP. Ceux-ci permettent de mettre à 0 certaines bits d'une adresse IP pour obtenir une adresse de sous-réseau. L'obtention de l'adresse de sous-réseau s'effectue avec un simple ET entre l'adresse IP et le masque de sous-réseau. Et ce n'est là qu'un exemple parmi tant d'autres !
Inverser un bit
modifierLe même raisonnement que pour les deux opérations précédentes vaut aussi pour ce qui est de l'inversion d'un bit. On obtient alors l'opération XOR à partir de la table de vérité.
Nombre | Masque | XORRésultat |
---|---|---|
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 0 |
Les champs de bits
modifierDans certains cas précis, il arrive que certaines informations soient codées sur un nombre limité de bits, qui peuvent être rassemblés dans un seul entier. De telles structures, formées en concaténant des bits dans un seul entier, sont appelées des champs de bits. Pour donner un exemple d'utilisation, supposons que j'ai regroupé plusieurs bits, dont chacun a une signification bien précise. Par exemple, je peux regrouper les droits d'accès dans un fichier dans un nombre : un des bits du nombre me dira alors si je peux écrire dans le fichier, un autre me dira si je peux le lire, un autre si… Bref, si jamais je veux modifier mes droits en écriture de mon fichier, je dois mettre un bit bien précis à 1 ou à 0 (suivant la situation). Cela peut se faire facilement en utilisant une instruction bit à bit entre ce nombre et une constante bien choisie.
Un autre cas typique est celui où un développeur compacte plusieurs données dans un seul entier. Par exemple, prenons le cas d'une date, exprimée sous la forme jour/mois/année. Un développeur normal stockera cette date dans trois entiers : un pour le jour, un pour le mois, et un pour la date. Mais un programmeur plus pointilleux sera capable d'utiliser un seul entier pour stocker le jour, le mois, et l'année. Pour cela, il raisonnera comme suit :
- un mois comporte maximum 31 jours : on doit donc encoder tous les nombres compris entre 1 et 31, ce qui peut se faire en 5 bits ;
- une année comporte 12 mois, ce qui tient dans 4 bits ;
- et enfin, en supposant que l'on doive gérer les années depuis la naissance de Jésus jusqu'à l'année 2047, 11 bits peuvent suffire.
Dans ces conditions, notre développeur décidera d'utiliser un entier de 32 bits pour le stockage des dates :
- les 5 bits de poids forts serviront à stocker le jour ;
- les 4 bits suivant stockeront le mois ;
- et les bits qui restent stockeront l'année.
Dans cette situation, le développeur qui souhaite modifier le jour ou le mois d'une date devra modifier une partie des bits, tout en laissant les autres intacts. Encore une fois, cela peut se faire facilement en utilisant une instruction bit à bit entre ce nombre et une constante bien choisie.
Supposons que le développeur veuille récupérer le jour stocké dans cette date. Comment doit-il s'y prendre ? Rien de plus simple : il suffit de mettre tous les autres bits du nombre à zéro, et de décaler le tout pour placer le résultat sur le bit de poids faible. Il suffit juste de choisir le bon décalage, et la bonne constante. Toutefois, cette méthode a un défaut : les constantes utilisées sont souvent très longues, ce qui empêche d'utiliser le mode d'adressage immédiat pour celles-ci. Raccourcir ces constantes permet de gagner pas mal de place. On peut ruser un petit peu en effectuant le décalage avant la mise à zéro.
Le cas le plus simple est quand le développeur souhaite récupérer un seul bit. Par exemple, si je veux récupérer le 5 éme bit en partant de la droite dans le nombre 1111 0011. Il me suffit de mettre tous les bits à zéro, sauf le bit voulu. Pour cela, je dois effectuer un AND entre 1111 0011 et la constante 0001 0000. J'obtiens alors 0001 0000. Ensuite, un décalage logique de 4 rangs vers la droite me donnera le bit voulu : 0000 0001. Autre exemple : je décale mon nombre 1111 0011 de 4 rangs vers la droite. J'obtiens 0000 1111. Ensuite, je mets tous les bits à zéro, sauf le bit de poids faible. La constante à utiliser est alors 0000 0001. Dans cette situation, on remarque que la constante à utiliser est très courte : c'est 1. Cette constante est très courte, ce qui permet d'utiliser le mode d'adressage immédiat pour l'instruction AND. De quoi gagner quelques cycles.