La manipulation de bits de bas niveau n'a jamais été mon point fort. J'apprécierai un peu d'aide pour comprendre le cas d'utilisation suivant d'opérateurs bitwise.Consider ...
int age, gender, height, packed_info;
. . . // Assign values
// Pack as AAAAAAA G HHHHHHH using shifts and "or"
packed_info = (age << 8) | (gender << 7) | height;
// Unpack with shifts and masking using "and"
height = packed_info & 0x7F; // This constant is binary ...01111111
gender = (packed_info >> 7) & 1;
age = (packed_info >> 8);
Je ne suis pas sûr de ce que ce code accomplit et comment? Pourquoi utiliser le nombre magique 0x7F? Comment se passe l'emballage et le déballage?
Comme le commentaire l'indique, nous allons regrouper l'âge, le sexe et la taille dans 15 bits du format suivant:
AAAAAAAGHHHHHHH
Commençons par cette partie:
(age << 8)
Pour commencer, age a ce format:
age = 00000000AAAAAAA
où chaque A peut être 0 ou 1.
<< 8
déplace les bits de 8 places vers la gauche et remplit les espaces vides avec des zéros. Donc vous obtenez:
(age << 8) = AAAAAAA00000000
De même:
gender = 00000000000000G
(gender << 7) = 0000000G0000000
height = 00000000HHHHHHH
Maintenant, nous voulons les combiner en une seule variable. L’opérateur |
vérifie chaque bit et renvoie 1 si le bit est 1 dans l’une des entrées. Alors:
0011 | 0101 = 0111
Si un bit est 0 dans une entrée, vous obtenez le bit de l'autre entrée. En regardant (age << 8)
, (gender << 7)
et height
, vous verrez que si un bit est 1 pour l'un de ces éléments, il est 0 pour les autres. Alors:
packed_info = (age << 8) | (gender << 7) | height = AAAAAAAGHHHHHHH
Maintenant, nous voulons décompresser les bits. Commençons par la hauteur. Nous voulons obtenir les 7 derniers bits et ignorer les 8 premiers. Pour ce faire, nous utilisons l'opérateur &
, qui ne retourne 1 que si les deux bits d'entrée sont à 1. Donc:
0011 & 0101 = 0001
Alors:
packed_info = AAAAAAAGHHHHHHH
0x7F = 000000001111111
(packed_info & 0x7F) = 00000000HHHHHHH = height
Pour obtenir l’âge, il suffit de tout déplacer de 8 places vers la droite et il nous reste 0000000AAAAAAAA
. Donc age = (packed_info >> 8)
.
Enfin, pour obtenir le genre, nous poussons tout 7 places vers la droite pour nous débarrasser de la hauteur. Nous nous soucions alors que du dernier bit:
packed_info = AAAAAAAGHHHHHHH
(packed_info >> 7) = 0000000AAAAAAAG
1 = 000000000000001
(packed_info >> 7) & 1 = 00000000000000G
Cela pourrait être une leçon assez longue en manipulation de bits, mais laissez-moi d'abord vous indiquer aussi le article de masquage de bits sur Wikipedia .
packed_info = (age << 8) | (gender << 7) | height;
Prenez l'âge et déplacez sa valeur sur 8 bits, puis prenez le sexe et déplacez-le sur 7 bits et la hauteur occupera les derniers bits.
age = 0b101
gender = 0b1
height = 0b1100
packed_info = 0b10100000000
| 0b00010000000
| 0b00000001100
/* which is */
packed_info = 0b10110001100
La décompression fait l'inverse mais utilise des masques comme 0x7F (qui est 0b 01111111) pour supprimer les autres valeurs du champ.
gender = (packed_info >> 7) & 1;
Travaillerait comme ...
gender = 0b1011 /* shifted 7 here but still has age on the other side */
& 0b0001
/* which is */
gender = 0b1
Notez que tout ce qui est AND à 1 équivaut à "garder" ce bit et AND à 0 équivaut à "ignorer" ce bit.
Si vous vouliez stocker une date sous forme de nombre, vous le feriez peut-être en multipliant l'année par 10 000, le mois par 100 et en ajoutant le jour. Une date telle que le 2 juillet 2011 serait codée sous le numéro 20110702:
year * 10000 + month * 100 + day -> yyyymmdd
2011 * 10000 + 7 * 100 + 2 -> 20110702
Nous pouvons dire que nous avons encodé la date dans un masque yyyymmdd. Nous pourrions décrire cette opération comme
C'est la même chose qui se passe avec l'encodage de l'âge, du sexe et de la hauteur, sauf que l'auteur pense en binaire.
Voir les plages que ces valeurs peuvent avoir:
age: 0 to 127 years
gender: M or F
height: 0 to 127 inches
Si nous traduisons ces valeurs en binaire, nous aurions ceci:
age: 0 to 1111111b (7 binary digits, or bits)
gender: 0 or 1 (1 bit)
height: 0 to 1111111b (7 bits also)
En gardant cela à l'esprit, nous pouvons encoder les données âge-sexe-hauteur avec le masque aaaaaaaghhhhhhh, mais nous parlons ici de binairedigits, pas décimaldigits.
Alors,
En binaire, l'opérateur Shift-Left (<<) déplace une valeur npositions vers la gauche. L'opérateur "Ou" ("|" dans de nombreuses langues) combine des valeurs ensemble. Par conséquent:
(age << 8) | (gender << 7) | height
Maintenant, comment "décoder" ces valeurs?
C'est plus facile en binaire qu'en décimal:
L'opérateur Shift-Right (>>) déplace une valeur de n positions vers la droite (les chiffres décalés "out" de la position la plus à droite sont perdus). L'opérateur binaire "And" ("&" dans de nombreuses langues) masque les bits. Pour ce faire, il faut un masque indiquant les bits à conserver et les bits à détruire (1 bits sont préservés). Donc:
height = value & 1111111b (preserve the 7 rightmost bits)
gender = (value >> 1) & 1 (preserve just one bit)
age = (value >> 8)
Puisque 1111111b en hexadécimal est 0x7f dans la plupart des langues, c'est la raison de ce nombre magique. Vous auriez le même effet en utilisant 127 (ce qui correspond à 1111111b en décimal).
Une réponse plus condensée:
AAAAAAA G HHHHHHH
Emballage:
packed = age << 8 | gender << 7 | height
Sinon, vous pouvez simplement additionner des composants si, par exemple, lorsqu'ils sont utilisés dans la fonction d'agrégation MySQL SUM
packed = age << 8 + gender << 7 + height
Déballage:
age = packed >> 8 // no mask required
gender = packed >> 7 & ((1 << 1) - 1) // applying mask (for gender it is just 1)
height = packed & ((1 << 7) - 1) // applying mask
Un autre exemple (plus long):
Supposons que vous souhaitiez créer une adresse IP, mais qu’il s’agit d’une adresse IP fictive, par exemple 132.513.151.319. Notez que certains composants supérieurs à 256, ce qui nécessite plus de 8 bits, contrairement aux adresses IP réelles.
Nous devons d’abord déterminer le décalage que nous devons utiliser pour pouvoir stocker le nombre maximal. Disons qu'avec des adresses IP fictives, aucun composant ne peut être plus grand que 999, ce qui signifie que nous avons besoin de 10 bits de stockage par composant ( permet des nombres jusqu'à 1014).
packed = (comp1 << 0 * 10) | (comp1 << 1 * 10) | (comp1 << 2 * 10) | (comp1 << 3 * 10)
Ce qui donne dec 342682502276
ou bin 100111111001001011110000000010010000100
Maintenant, décompressons la valeur
comp1 = (packed >> 0 * 10) & ((1 << 10) - 1) // 132
comp2 = (packed >> 1 * 10) & ((1 << 10) - 1) // 513
comp3 = (packed >> 2 * 10) & ((1 << 10) - 1) // 151
comp4 = (packed >> 3 * 10) & ((1 << 10) - 1) // 319
Où (1 << 10) - 1
est un masque binaire que nous utilisons pour masquer les bits à gauche au-delà des 10 bits les plus à droite qui nous intéressent.
Même exemple utilisant une requête MySQL
SELECT
(@offset := 10) AS `No of bits required for each component`,
(@packed := (132 << 0 * @offset) |
(513 << 1 * @offset) |
(151 << 2 * @offset) |
(319 << 3 * @offset)) AS `Packed value (132.513.151.319)`,
BIN(@packed) AS `Packed value (bin)`,
(@packed >> 0 * @offset) & ((1 << @offset) - 1) `Component 1`,
(@packed >> 1 * @offset) & ((1 << @offset) - 1) `Component 2`,
(@packed >> 2 * @offset) & ((1 << @offset) - 1) `Component 3`,
(@packed >> 3 * @offset) & ((1 << @offset) - 1) `Component 4`;
L'opérateur de décalage à gauche signifie "multiplier par deux, autant de fois". En binaire, multiplier un nombre par deux équivaut à ajouter un zéro sur le côté droit.
L'opérateur de décalage droit est l'inverse de l'opérateur de décalage gauche.
L'opérateur de canal est "ou", ce qui signifie de superposer deux nombres binaires l'un sur l'autre. S'il y a un 1 dans l'un ou l'autre nombre, le résultat dans cette colonne est un 1.
Alors, extrayons l'opération pour emballée_info:
// Create age, shifted left 8 times:
// AAAAAAA00000000
age_shifted = age << 8;
// Create gender, shifted left 7 times:
// 0000000G0000000
gender_shifted = gender << 7;
// "Or" them all together:
// AAAAAAA00000000
// 0000000G0000000
// 00000000HHHHHHH
// ---------------
// AAAAAAAGHHHHHHH
packed_info = age_shifted | gender_shifted | height;
Et le déballage est l'inverse.
// Grab the lowest 7 bits:
// AAAAAAAGHHHHHHH &
// 000000001111111 =
// 00000000HHHHHHH
height = packed_info & 0x7F;
// right shift the 'height' bits into the bit bucket, and grab the lowest 1 bit:
// AAAAAAAGHHHHHHH
// >> 7
// 0000000AAAAAAAG &
// 000000000000001 =
// 00000000000000G
gender = (packed_info >> 7) & 1;
// right shift the 'height' and 'gender' bits into the bit bucket, and grab the result:
// AAAAAAAGHHHHHHH
// >> 8
// 00000000AAAAAAA
age = (packed_info >> 8);
Vous pouvez voir l'expression x & mask
comme une opération qui supprime de x
les bits qui ne sont pas présents (c'est-à-dire dont la valeur est 0) dans mask
. Cela signifie que packed_info & 0x7F
supprime de packed_info
tous les bits supérieurs au septième bit.
Exemple: si packed_info
est 1110010100101010
en binaire, alors packed_info & 0x7f
sera
1110010100101010
0000000001111111
----------------
0000000000101010
Donc, dans height
nous obtenons les 7 bits inférieurs de packed_info
.
Ensuite, nous décalons le packed_info
entier de 7, de cette manière, nous supprimons les informations que nous avons déjà lues. Donc nous obtenons (pour la valeur de l'exemple précédent) 111001010
Le genre est stocké au bit suivant, donc avec la même astuce: & 1
, nous extrayons uniquement ce bit de l'information. Le reste des informations est contenu au décalage 8.
Emballer n'est pas compliqué non plus: vous prenez age
, déplacez-le de 8 bits (pour obtenir 1110010100000000
de 11100101
), décaler la gender
de 7 (afin d'obtenir 00000000
), et prenez la hauteur (en supposant qu'il convienne pour les 7 bits inférieurs) . Ensuite, vous les composez tous ensemble:
1110010100000000
0000000000000000
0000000000101010
----------------
1110010100101010
Même exigence que j'ai rencontrée à plusieurs reprises. C'est très facile avec l'aide de Bitwise AND operator. Il suffit de qualifier vos valeurs avec des puissances croissantes de deux (2). Pour stocker plusieurs valeurs, AJOUTEZ leur nombre relatif (puissance de 2) et obtenez le SOMME. Ce SUM va consolider vos valeurs sélectionnées. COMMENT ?
Il suffit de faire Bitwise AND avec chaque valeur et cela donnera zéro (0) pour les valeurs non sélectionnées et non nul pour lesquelles sont sélectionnées.
Voici l'explication:
1) Valeurs (OUI, NON, PEUT-ÊTRE)
2) Affectation à la puissance de deux (2)
YES = 2^0 = 1 = 00000001
NO = 2^1 = 2 = 00000010
MAYBE = 2^2 = 4 = 00000100
3) Je choisis OUI et PEUT-ÊTRE D'où SUM:
SUM = 1 + 4 = 5
SUM = 00000001 + 00000100 = 00000101
Cette valeur stockera à la fois OUI et PEUT-ÊTRE. COMMENT?
1 & 5 = 1 ( non zero )
2 & 5 = 0 ( zero )
4 & 5 = 4 ( non zero )
Donc SUM consiste en
1 = 2^0 = YES
4 = 2^2 = MAYBE.
Pour des explications plus détaillées et la mise en œuvre, visitez mon blog