ビット演算
ビット演算は、数値を二進数で表した時に、それぞれのビットに対して行われるような演算のことです。
ここでは、上記のプログラムで用いている「右シフト」と「論理積」を解説します。
右シフト
右シフトは、二進数の各ビットをLSB側(通常の二進数表記をした場合の右側)にずらす演算で、
0b00100010>>5
Code language: Arduino (arduino)
のように>>という演算子で表されます。
例えば、0b00100010という二進数(「A」の点灯パターンの4行目)の右にひとつシフトすると、
二進数の各ビット | 十進数表記 | |||||||||
---|---|---|---|---|---|---|---|---|---|---|
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |||
元の値 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 34 | |
右シフト後の値 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 17 |
のようになり、0b00010001という値が得られます。
例で示した 0b00100010>>5
は、0b00100010(=34)を右に5つずらすことを意味し、
二進数の各ビット | 十進数表記 | |||||||||
---|---|---|---|---|---|---|---|---|---|---|
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |||
元の値 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 34 | |
右に1つシフト後の値 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 17 | |
右に2つシフト後の値 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 8 | |
右に3つシフト後の値 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 4 | |
右に4つシフト後の値 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 2 | |
右に5つシフト後の値 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
のように結果は0b00000001(= 1 )になります。上記のとおり、LSB側にあふれたビットは破棄されます。
論理積(AND)
ビット演算における論理積(AND)は、2つの二進数の各ビットをビットごとに掛け合わせる演算で、
0b00100010&1
Code language: Arduino (arduino)
のように&
という演算子で表されます(なお、&
は前に数値がつかない場合は「後ろの変数のアドレスを示す」という全く別の意味になるのでご注意ください)。
論理積では、2つ二進数双方のビットが1の場合にのみ結果のビットが1になり、片方でも0の場合は0になります。表にまとめると以下のようになります。
入力値のビットの値 | 出力値のビットの値 | |
---|---|---|
値その1 | 値その2 | |
0 | 0 | 0 |
0 | 1 | 0 |
1 | 0 | 0 |
1 | 1 | 1 |
例えば 0b01100111(= 103 )という値と 0b00110001(= 49 )という値の論理積を取ると、
二進数の各ビット | 十進数表記 | |||||||||
---|---|---|---|---|---|---|---|---|---|---|
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |||
値その1 | 0 | 1 | 1 | 0 | 0 | 1 | 1 | 1 | 103 | |
値その2 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 49 | |
論理積 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 33 |
のように、双方で1になっているビットのみが1を保持して結果は0b00100001(=33)になります。
例で示した0b00100010&1
は、0b00100010(=34)と 1(=0b00000001)の論理積を取っており、
二進数の各ビット | 十進数表記 | |||||||||
---|---|---|---|---|---|---|---|---|---|---|
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |||
値その1 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 34 | |
値その2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | |
論理積 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
のように、結果は0となります。
このように、ある値と1の論理和を取ると、その値のLSB(0ビット目)のみを取り出すことができ、右シフトと組み合わせることで特定のビットが0か1かを取得するのに便利です。
演算の優先度
ビット演算を含むC言語の演算には優先度があります。また、演算によって結合規則(左から右か、右から左か)が異なります。
例えば、右シフトは論理積よりも優先されるので、「0b00100010を右に5シフトし、1との論理積を取る」という演算は、以下のいずれの記載も可能です。
0b00100010>>5&1; // 0b00100010を右に5シフトし、1との論理積を取る(1になる)
1&0b00100010>>5; // 0b00100010を右に5シフトし、1との論理積を取る(1になる)
Code language: Arduino (arduino)
2番目のものは「1と0b00100010の論理積を取り、右に5シフトする」という演算にはならないので注意しましょう。
どこが先に計算されるべきかを確実にするには、以下のように括弧を付けておくのが良いでしょう。
(0b00100010>>5)&1; // 0b00100010を右に5シフトし、1との論理積を取る(1になる)
1&(0b00100010>>5); // 0b00100010を右に5シフトし、1との論理積を取る(1になる)
(1&0b00100010)>>5; // 1と0b00100010の論理積を取り、右に5シフトする(0になる)
Code language: Arduino (arduino)
それでは、ビット演算を用いたプログラムを組んでみましょう。プログラムが完成し動作確認を行ったら、次に示すプログラム例と見比べてみましょう。