Les opérations sur les bits nous permettent d'emmagasiner plusieurs
informations dans une valeur, en VBA. Le concept est très voisin de celui
utilisé en Win32 API, où un argument drapeau (flag) peut représenter un
ensemble de conditions.
4 bits, un demi-octet, peuvent représenter jusqu'à 16 nombres
(j'ignore, pour l'instant, la possibilité d'utiliser un signe) car chaque bit peut prendre la valeur 0 ou la valeur 1. En fait, voici ces 16 possibilités.
0000, 0001, 0010, 0011,
0100, 0101, 0110, 0111,
1000, 1001, 1010, 1011,
1100, 1101, 1110, 1111
Si on considère qu'il s'agit là de nombres entiers positifs, en base
deux, leur conversion décimale est simplement:-
0, 1, 2, 3,
4, 5, 6, 7,
8, 9, 10, 11,
12, 13, 14, 15
Si on ne retient que les combinaisons qui ne possèdent qu'un seul un, et
trois zéros, on trouve:
0001 (= 1)
0010 (= 2)
0100 (= 4)
1000 (= 8)
On peut s'amuser à additionner ces nombres entre eux pour générer tous
les autres nombres, comme par exemple
1 + 2 = 3 (0001 + 0010 = 0011)
1 + 4 = 5 (0001 + 0100 = 0101)
4 + 8 = 12 (0100 + 1000 = 1100)
et ainsi de suite...
1 + 2 + 4 + 8 = 15 ( 0001 + 0010 + 0100 + 1000 =1111)
Maintenant, l'utilisation de masque implique l'utilisation de l'opérateur AND, ce qui nous permet de spécifier quels bits peuvent être visibles, ou sélectionnés. En effet, si le bit "i" est 1e bit correspondant du second argument sera choisi, alors que si le bit "i" est zéro, un zéro sera dans le résultat, indépendamment du bit correspondant du second arguement. Prenons un exemple pour illustrer
? ( 7 AND 10)
Retourne 2 car si on décompose les arguments en bits, on trouve (0111) AND (1010). Si on considère que le masque est le premier
argument (on obtient le même résultat en inversant les rôles), il signifie
donc de ne pas considérer le premier bit du second
argument, prenant 0. Ensuite, il dit de prendre le second bit du second argument, de même que le troisième et le
quatrième. On se retrouve donc avec quatre bits, 0 0 1 0, soit, le nombre 2. On peut également voir le tout comme si chaque bit serait soumis à l'opérateur AND avec son correspondant.
7 = 0 1 0 0
10 = 1 0 1 0
Premiers bits: 0 AND 1 retourne 0
bits en position 2: 1 AND 0 retourne 0
bits en position 3: 1 AND 1 retourne 1
bits en position 4: 0 AND 0 retourne 0
Résultat: 0 0 1 0 = 2
Ce qui nous confirme qu'on est justifié de considérer qu'un des deux arguments du AND peut être perçu comme un "masque" pour le second argument.
Cette façon de voir les choses peut être plus instructive que celle où on ne considère qu'un AND appliqué bit à bit. Supposons, par exemple, que nous sommes une agence de recrutement et que nos clients sont intéressés à connaître le sexe de nos candidats, s'ils sont déjà employés, s'ils savent conduire et s'ils cherchent un emploi à temps plein. Nous pouvons donc définir les "propriétés" suivantes pour nos candidats:
Const IsFemale = 1
Const IsEmployed = 2
Const CanDrive = 4
Const FullTime = 8
Si on désire décrire un candidat mâle, ayant actuellement un emploi, sans permis de conduire mais cherchant un emploi à temps plein, on utilisera
IsEmployed + FullTime ( = 2+8 = 10)
(pour éviter des erreurs, on utilisera plutôt OR que +. En effet, IsEmployed+IsEmployed+FullTime retourne 12, soit FullTime+CanDrive. Par contre IsEmployed OR IsEmployed OR FullTime retourne 10, en
dépits de l'inclusion répétée de IsEmployed)
Donc, pour notre masque, si le cumul des propriétés de nos candidats est dans la variable AppProps, alors pour chercher si AppProps contient FullTime, on demande
? (AppProps AND FullTime) = FullTime
et de même, on peut vérifier s'il s'agit d'une femme avec:
? (AppProps AND IsFemale) = IsFemale
ou on peut faire les deux recherches en une seule "ligne" comme suit:
? ( AppProps AND (IsEmployed OR IsFemale)) = (IsEmployed OR IsFemale)
ce qui peut être un peu contre intuitif, car on désire et quelqu'un déjà employé et un femme, et, pour se faire, on utilise OR. En fait, si on applique (AppProps AND (IsEmployed OR IsFemale)) à un homme ayant un emploi,
le résultat est IsEmployed. Mais puisque IsEmployed <> (IsEmployed OR IsFemale), un tel candidat ne sera pas conservé. Seuls les candidats ayant IsEmployed et IsFemale
seront donc bel et bien conservés par cette expression.
Voici un bout de code qui permet d'illustrer ces diverses techniques.
Const IsFemale = 1
Const IsEmployed = 2
Const CanDrive = 4
Const FullTime = 8
Function TestBitMasks()
Dim FredProps As Byte
Dim JoanProps As Byte
FredProps = IsEmployed + FullTime
Debug.Print "Fred is FullTime?: "; _
(FredProps And FullTime) = FullTime
Debug.Print "Fred is Female?: "; _
(FredProps And IsFemale) = IsFemale
Debug.Print "Fred is FullTime and Female?: "; _
(FredProps And (IsFemale Or FullTime)) = (IsFemale Or FullTime)
JoanProps = IsEmployed + FullTime + IsFemale
Debug.Print "Joan is FullTime?: "; _
(JoanProps And FullTime) = FullTime
Debug.Print "Joan is Female?: "; _
(JoanProps And IsFemale) = IsFemale
Debug.Print "Joan is FullTime and Female?: "; _
(JoanProps And (IsFemale Or FullTime)) = (IsFemale Or FullTime)
End Function