最近は生徒さんからの質問も増えてきて、先日は電池の酸化還元反応について問われました。まあ、プログラミングとは直接関係ありませんが。
今回はビット演算の話です。
コンピュータの内部では2進数が使われています。
すなわち0と1の2種類の数字を使って計算しています。
2進数で表示したときの1桁が1ビットです。8ビットは1バイトと呼ばれます。
情報の最小単位がビットで、情報の基本単位がバイトです。
通常のプログラミングではあまり行われなくなってきましたが、ビット単位で演算をすることができます。
ここではProcessingを使ってビット演算を試してみましょう。
binary()で2進数表示ができます。
■AND
両方のビットが1のときのみ結果が1になります。
例えば、aに123を、bに456を代入して a AND b の演算を行うと下記のような結果が得られます。
a 123 00000000000000000000000001111011
b 456 00000000000000000000000111001000
a AND b 00000000000000000000000001001000
■OR
いずれかのビットが1なら結果が1になります。
例えば、
a 123 00000000000000000000000001111011
b 456 00000000000000000000000111001000
a OR b 00000000000000000000000111111011
といった具合です。
■XOR
両方のビットが異なるときに1 になります。
例えば、
a 123 00000000000000000000000001111011
b 456 00000000000000000000000111001000
a XOR b 00000000000000000000000110110011
のようになります。
■左シフト
1ビット左にシフトすると2倍することができます。
例えば、nに123を代入して左シフトしてみます。
n 123 00000000000000000000000001111011
n << 1 246 00000000000000000000000011110110
nは246になりました。
■右シフト
1ビット右にシフトすると2で割ることができます。
例えば、先ほどのnを右シフトしてみます。
n 246 00000000000000000000000011110110
n >> 1 123 00000000000000000000000001111011
nは123になりました。
■1の補数
2進数の場合、補数は2つあります。1の補数と2の補数です。
元の数に1の補数を補うと2進数の最大値になります。
1の補数はビットを反転することで得られます。
例えば、mに123を代入してビット反転してみます。
m 123 00000000000000000000000001111011
~m -124 11111111111111111111111110000100
■2の補数
負の数を表現する方法です。1の補数に1を加えることで得られます。
2の補数を元の数に補うと桁が上がります。
そして、桁上がりしたビットは無視されるため0になります。
補ったら0になるということは元の数の対となる負数と言えるわけです。
最上位ビットは符号ビットと呼ばれます。
例えば、先ほどのmの2の補数を求めてみましょう。
m 123 00000000000000000000000001111011
~m+1 -123 11111111111111111111111110000101
負の数 -123 が得られました。
以上をProcessingのコードにすると以下のようになります。
void setup() { noLoop(); } void draw() { // bitwise AND // 両方のビットが1のときのみ結果が1 println("bitwise AND"); int a = 123; int b = 456; println("a ", a, binary(a)); println("b ", b, binary(b)); int c = a & b; println("a AND b", binary(c)); println(); // bitwise OR // いずれかのビットが1なら結果が1 println("bitwise OR"); println("a ", a, binary(a)); println("b ", b, binary(b)); c = a | b; println("a OR b", binary(c)); println(); // bit XOR // 両方のビットが異なるときに1 println("bit XOR"); println("a ", a, binary(a)); println("b ", b, binary(b)); c = a^b; println("a XOR b", binary(c)); println(); // bit left shift // 2倍する println("bit left shift"); int n = 123; println("n ", n, binary(n)); n = n << 1; println("n << 1", n, binary(n)); println(); // bit left shift // 2で割る println("bit right shift"); println("n ", n, binary(n)); n = n >> 1; println("n >> 1", n, binary(n)); println(); // bit INVERT 1の補数 // 1の補数を補うと2進数の最大値になる println("bit INVERT"); int m = 123; println("m ", m, binary(m)); m = ~m; println("~m ", m, binary(m)); println(); // bit INVERT 2の補数 // 負の数を表現する方法 // 2の補数を補うと桁が上がる // 桁上がりしたビットは無視されるため0になる // 補ったら0になるということは元の数の対となる負数と言える // 最上位ビットは符号ビット println("bit INVERT + 1"); m = 123; println("m ", m, binary(m)); m = ~m + 1; println("~m + 1", m, binary(m)); }