ビット演算とは、二進数同士のちょっと変わった計算のことである。
通常、我々の日常生活では十進数を使っている。0~9の十種類を組み合わせて数を表現する方法だ。
二進数は1と0の二種類だけですべての数を表す方法だ。
たとえば、3は二進数では11、5は101となる。
対応表を作ると簡単に求まる。
下表の一番上の行は二進数の位を表す。
十進数↓ | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
3 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
5 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 |
93 | 0 | 1 | 0 | 1 | 1 | 1 | 0 | 1 |
27 | 0 | 0 | 0 | 1 | 1 | 0 | 1 | 1 |
たとえば3は、1と2で作れるので1の位と2の位に1を入れる。
5は4と1で作れるので101といった具合に埋めていけば良い。
この2進数の1桁のことをコンピューターではビットと呼ぶ。
これを使ったちょっと変わった計算が、ビット演算(または論理演算)と呼ばれる。
ビット演算にはいくつか種類があるが、代表的なものはANDとORだ。
ためしに3 AND 5というのをやってみよう。
ANDは、2つのビットのうち、両方が1のときに1となり、それ以外は0となる。
ケタをあわせて、位ごとにAND計算する。
3 = 0 1 1
5 = 1 0 1
4の位は0 And 1 = 0
2の位は1 And 0 = 0
1の位は1 And 1 = 1
よって、答えは001となる。十進数に直しても1である。
次にOR運算を紹介する。
ORは、どちらかのビットが1なら1、両方0のときは0になる。
計算過程は省略するが、結果は111となる。
十進では4+2+1=7となる。
実はこの計算、10進のままでよければVBAでも簡単にできる。
Sub test10() Debug.Print 3 And 5 '結果は1 Debug.Print 3 Or 5 '結果は7 End Sub
VBAでは2進数を扱えないため、2進数は文字列で表現することになるが、2進数文字列のまま計算するよりは、整数型の10進数に直して計算した後に2進数文字列に戻したほうがパフォーマンスは良いかもしれない。
また、VBAでは10進数→16進数文字列の変換関数Hex$が用意されている。
16進数から2進数への変換は、変換表を使えば究めて簡単である。
16進数の一桁が2進数の4桁と対応しているため、以下のようになる。
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
0000 | 0001 | 0010 | 0011 | 0100 | 0101 | 0110 | 0111 | 1000 | 1001 |
A | B | C | D | E | F | ||||
1010 | 1011 | 1100 | 1101 | 1110 | 1111 |
さて、試しに、5 And 12、5 Or 12、5 Xor 12をやってみた。
Dim BinArr(0 To 15) As String Sub BinArrInit() '初めから16進数の対応を用意しておく。 BinArr(0) = "0000" BinArr(1) = "0001" BinArr(2) = "0010" BinArr(3) = "0011" BinArr(4) = "0100" BinArr(5) = "0101" BinArr(6) = "0110" BinArr(7) = "0111" BinArr(8) = "1000" BinArr(9) = "1001" BinArr(10) = "1010" BinArr(11) = "1011" BinArr(12) = "1100" BinArr(13) = "1101" BinArr(14) = "1110" BinArr(15) = "1111" End Sub Sub Test() BinArrInit Debug.Print Hex2Bin(Hex$(5 And 12)) Debug.Print Hex2Bin(Hex$(5 Or 12)) Debug.Print Hex2Bin(Hex$(5 Xor 12)) End Sub Function Hex2Bin(h As String) As String Dim Result As String For i = 1 To Len(h) x = Mid(h, i, 1) Select Case Asc(x) Case Asc("0") To Asc("9") '0~9ならそのままBinArrに渡す Result = Result & BinArr(x) Case Asc("a") To Asc("f") 'a~fなら、10~15に変換して渡す Result = Result & BinArr(Asc(x) - Asc("a") + 10) Case Asc("A") To Asc("F") 'A~Fなら、10~15に変換して渡す Result = Result & BinArr(Asc(x) - Asc("A") + 10) Case Else Err.Raise 1000, , "引数に不正な文字が含まれています。" End Select Next Hex2Bin = Result End Function
Testプロシージャを実行すると、次の結果になる。
0100 1101 1001
Hex2Binで少しややこしい処理をしているので解説。
以下の部分である。
Case Asc("A") To Asc("F") 'A~Fなら、10~15に変換して渡す Result = Result & BinArr(Asc(x) - Asc("A") + 10)
Asc関数は文字コードを求めるもので、Aなら65、Bなら66となる。
ただ、ここで求めたいのは16進数である。Aは16進数で10となる。
以下の処理であるが、xが"A"のとき、65-65+10で10になる。
Asc(x) - Asc("A") + 10
xが"B"のとき、66-65+10で11になる。
xが"C"のとき、67-65+10で12になる。
つまり、Aとの差分を求めて、16進のAである10を足しているということ。
10の代わりに「&HA」としても良いけど、また余計な説明が増えるのでやめておいた。
ちなみにNotはビット演算とは挙動が違うのでオリジナルを作るしかない。今回Notはパス。