C言語のシフト演算とビット演算
概要
1. シフト演算について
1-1. 左シフト
1-2. 右シフト
2. ビット演算について
2-1. ANDの使用方法
2-2. ORの使用方法
2-3. XORの使用方法
1. シフト演算について
シフトは左シフト・右シフトがあり、CPUによってはローテートシフト(シフトして溢れたビットが反対側に現れる)も可能なものが存在します。
しかしC言語で「>>」や「<<」を使えば、ローテートではなく通常のシフトが使われるのが一般的です。
1-1. 左シフト
符号なし変数に対し、左へ1ビットシフトする事は、
値を2倍にすることに等しい。
[符号なし変数] 0001b << 1 (1) ---> 0010b (2) 0010b << 1 (2) ---> 0100b (4) 0100b << 1 (4) ---> 1000b (8)
符号付変数に対し、左へ1ビットシフトする時は、
その範囲に注意しなければなりません。
[符号付変数] 0001b << 1 (1) ---> 0010b (2) 0010b << 1 (2) ---> 0100b (4) 0100b << 1 (4) ---> 1000b (-8) *想定外の値になる可能性あり
1-2. 右シフト
符号なし変数に対し、右へ1ビットシフトする事は、
値を1/2にすることに等しい。
[符号なし変数] 1000b >> 1 ---> 0100b (4) 0100b >> 1 ---> 0010b (2) 0010b >> 1 ---> 0001b (1)
符号付変数に対し、右へ1ビットシフトする時は、
最上位ビット(MSB)の値に注意しなければなりません。
[符号付変数] 1000b >> 1 (-8) ---> 0100b (4) *想定外の値になる可能性あり 0100b >> 1 (4) ---> 0010b (2) 0010b >> 1 (2) ---> 0001b (1)
しかしC言語上で -8 >> 1と書いた場合、結果は-4になります。
これは算術シフトにより2の補数フォーマットが崩れないような形でシフトを行ってくれるからです。
具体的には次のようなシフトが行われます。
[符号付変数] 1000b >> 1 (-8) ---> 1100b (-4) 1100b >> 1 (-4) ---> 1110b (-2) 1110b >> 1 (-2) ---> 1111b (-1) 0111b >> 1 (+7) ---> 0011b (+3) 0011b >> 1 (+3) ---> 0001b (+1)
算術シフトは、シフト後もMSBビットが変化しないことに注意して下さい。
2. ビット演算について
注1 : 1ビット目を最下位ビットとして数えています
注2 : AND(ビット積)、OR(ビット和)、XOR(ビット差)
2-1. ANDの使用方法
ANDはビットのマスク処理によく使われます。
例1として4ビットの中の特定ビットだけを取り出したい場合を考えます。
例1
元のデータ:1001b
この中の最下位ビット(LSB)だけを取り出したい場合、
0001bとのANDを行うことで取り出すことができます。
1001b & 0001b -------------- 0001b
上位3ビットの内容は、0とのANDを行うことで、
必ず0になります。結果的にLSBビットだけを取り出すことができます。
例2
元のデータ:1100b
この中の3ビット目だけを取り出したい場合
0100bとのANDを行うことで取り出すことができます。
1100b & 0100b -------------- 0100b
例1では、取り出した結果値は0か1になりますが、
例2では0か4になることに注意して下さい。
つまり、取り出したビットが1であった場合、結果は0以外の値になりますので、
if等で判定させる場合、それに合わせる必要があります。
2-2. ORの使用方法
ORは特定ビットを1にする為に使用されます。
例1
元のデータ:1001b
この中の2ビット目を1にしたい場合
1001b | 0010b -------------- 1011b
のように使用します。用途としては、偶数を奇数にしたり、
特定ビットを立てなければ書き込めないポートへの書き込みに使用されます。
例2 (偶数から奇数変換)
1010b (10) | 0001b ------------- 1011b (11)
最下位ビットが1になることで、常に奇数にすることでできます。
2で割り切れたら1を足すというロジックでも出来ますが、それよりは遥かに高速です。
例3 (ポート出力)
あるポートに4ビットデータを書き込みたいが、上位4ビットが1でないと
書き込めないようにプロテクトされている場合、
0000 0101 (元のデータ) | 1111 0000 -------------------------- 1111 0101 (書き込みデータ)
上のように上位4ビットが常に1になり、書き込み条件を作ることができます。
これは組み込みでは重要なポートやレジスタに書き込む場合によく見られるようです。
2-3. XORの使用方法
XORはビット反転させたい場合やゼロクリアに使用されます。
例1 (反転)
元のデータ:1001b
1001b ^ 1111b ------------------ 0110b
例2 (クリア)
元のデータ:1001b
1001b ^ 1001b ------------------ 0000b
上のように同じデータ同士でXORを取れば、必ずゼロになります。
CPUによっては、ゼロ代入より高速であることから好んで使われたり、
コンパイラが最適化により自動変換したりするようです。
以上になります。
参考:
ビット演算 - Wikipedia
職業としてのプログラミング ビットシフトの落とし穴 - 算術シフトと論理シフト