Les chiffres binaires correspondent en définitive directement aux transistors de ton processeur ou de ta mémoire vive, et à l'état de chacun d'eux, « allumé » ou « éteint ».
La manipulation de bas niveau, que l'on appelle familièrement « bidouillage de bits », est particulièrement importante dans les langages système.
Les langages de haut niveau comme Julia masquent généralement la plupart de ces détails. Cela dit, tout un éventail d'opérations sur les bits est disponible dans le langage de base.
Note : Pour voir une sortie binaire lisible par un humain dans le REPL, presque tous les exemples ci-dessous doivent être enveloppés dans une fonction bitstring(). Cela distrait visuellement, donc la plupart des occurrences de cette fonction ont été retirées.
Les types entiers, signés ou non signés, peuvent être représentés par une string de 1 et de 0.
julia> bitstring(UInt8(5))
"00000101"
Les décalages de bits se contentent de tout déplacer vers la gauche ou vers la droite d'un nombre de positions donné. Avec les types UInt, certains bits disparaissent d'un côté, et l'autre côté est complété par des zéros :
julia> ux::UInt8 = 5
5
julia> bitstring(ux)
"00000101"
julia> ux << 2 # left by 2
"00010100"
julia> ux >> 1 # right by 1
"00000010"
Chaque décalage vers la gauche double la valeur et chaque décalage vers la droite la divise par deux (sous réserve de troncature). C'est plus évident en représentation décimale :
julia> 3 << 2
12
julia> 24 >> 3
3
Ce type de décalage est bien plus rapide qu'une arithmétique « véritable », ce qui rend la technique très populaire dans la programmation de bas niveau.
Avec les entiers signés, il faut être un peu plus prudent.
Les décalages vers la gauche sont relativement simples :
julia> sx = Int8(5)
5
julia> sx # positive integer
"00000101"
julia> sx << 2
"00010100"
julia> -sx # negative integer
"11111011"
julia> -sx << 2
"11101100"
Décaler vers la gauche un entier signé positif revient donc au même que pour un entier non signé.
Les valeurs négatives sont stockées sous forme de complément à deux, ce qui signifie que le bit le plus à gauche vaut 1. Aucun problème pour un décalage vers la gauche, mais alors comment compléter les bits les plus à gauche lors d'un décalage vers la droite ?
julia> sx >> 2 # simple for positive values!
"00000001"
julia> -sx # negative integer
"11111011"
julia> -sx >> 2 # pad with repeated sign bit
"11111110"
julia> -sx >>> 2 # pad with 0
"00111110"
L'opérateur >> effectue un décalage arithmétique, en préservant le bit de signe.
L'opérateur >>> effectue un décalage logique, en complétant par des zéros comme si le nombre n'était pas signé.
On a vu dans un concept précédent que les opérateurs && (et), || (ou) et ! (pas) s'utilisent avec des valeurs booléennes.
Il existe des opérateurs équivalents & (et bit à bit), | (ou bit à bit) et ~ (un tilde, négation bit à bit) pour comparer les bits de deux entiers.
julia> 0b1011 & 0b0010 # bit is 1 in both numbers
"00000010"
julia> 0b1011 | 0b0010 # bit is 1 in at least one number
"00001011"
julia> ~0b1011 # flip all bits
"11110100"
julia> xor(0b1011, 0b0010) # bit is 1 in exactly one number, not both
"00001001"
Ici, xor() est le ou exclusif, utilisé comme une fonction.
Ton ami vient de t'envoyer un message contenant un secret important. Comme ton ami ne voulait pas que les autres puissent le lire facilement, le message a été chiffré en effectuant une série de manipulations de bits. Tu vas devoir écrire les fonctions qui t'aideront à déchiffrer le message.
La première étape pour déchiffrer le message consiste à annuler le décalage effectué lors du chiffrement en décalant les bits vers la droite. D'autres étapes du processus de déchiffrement partiront du principe que des 0 sont insérés depuis la gauche.
Implémente la fonction shift_back() qui prend une valeur et le nombre de positions de décalage, et effectue le décalage.
shift_back(0b1001, 2) # => 0b0010
Ensuite, certains bits doivent être mis à 1.
Implémente la fonction set_bits() qui prend une valeur et un masque et renvoie le résultat de la mise à 1 des bits de la valeur.
Un bit de value doit être mis à 1 lorsque le bit du mask est aussi à 1.
Tous les autres bits doivent rester inchangés.
set_bits(0b0110, 0b0101) # => 0b0111
Certains bits sont inversés pendant le chiffrement. Il faudra les inverser à nouveau pour déchiffrer le message.
Implémente la fonction flip_bits() qui prend une valeur et le masque.
Le masque indique quels bits de la valeur inverser.
Si le bit vaut 1 dans mask, le bit est inversé dans value.
Tous les autres bits restent inchangés.
flip_bits(0b1100, 0b0101) # => 0b1001
Enfin, certains bits valent toujours 0 après le déchiffrement.
Implémente la fonction clear_bits() qui prend une valeur et un masque.
Les bits de value doivent être mis à 0 lorsque le bit du masque vaut 1.
Tous les autres bits doivent rester inchangés.
clear_bits(0b0110, 0b0101) # => 0b0010
Inscris-toi sur Exercism pour apprendre et maîtriser Julia avec 35 concepts128 exercices, et un vrai mentorat humain, le tout gratuitement.