C# のビット演算
C# の 2 進数表記
C# では 2 進数の数値のリテラルには、接頭辞 0b を付けます。
例えば 10進数の 5 は 2 進数で 101 ですが、これを C# のコード内で記述するには、 0b101 とします。
次の例では 2 進数 0b101 を変数 x にセットし、 その 10進数の値とビット列を表示しています。
using System;
class Program
{
static void Main(string[] args)
{
int x = 0b101;
Console.WriteLine($"{x}: {Bin(x)}");
// 5: 00000000000000000000000000000101
}
static string Bin(int x)
{
var s = Convert.ToString(x, toBase: 2);
return s = s.PadLeft(sizeof(int) * 8, '0');
}
}
ビット列を文字列で表示するために、 Convert.ToString() メソッドを利用しています。 toBase: 2 を指定することで 2 進数の表記として文字列を取得できます。
C# のビット演算は int 型 (System.Int32) に対して定義されています。 byte などの他の型で使う場合はキャストして使います。
C# の NOT 演算 (補数演算)
C# での NOT 演算子は単項演算子の ~ です。 NOT 演算 (論理否定) は数値の各ビットに対して、1 は 0 にし、 0 は 1 にします。
ビットを反転させた NOT は元の数の補数といいます。このため ~ は補数演算子 (complement operator) ともいいます。
次の例では元の数値 x の NOT 演算 ~x を y にセットしています。
int x = 0b1010100;
int y = ~x;
Console.WriteLine($"x:{Bin(x)}");
Console.WriteLine("".PadLeft(34, '-'));
Console.WriteLine($"y:{Bin(y)}");
// x:00000000000000000000000001010100
// ----------------------------------
// y:11111111111111111111111110101011
y では x のビットが全て反転していることがわかります。
C# の OR 演算
C# での OR 演算子は | です。
OR 演算では二つのビット列で桁ごとにビット値を比較して、いずれかが 1 であれば 1、 両方とも 0 の場合に 0 とします。
int x = 0b101100;
int y = 0b011001;
int z = x | y;
Console.WriteLine($"x:{Bin(x)}");
Console.WriteLine($"y:{Bin(y)}");
Console.WriteLine("".PadLeft(34, '-'));
Console.WriteLine($"z:{Bin(z)}");
// x:00000000000000000000000000101100
// y:00000000000000000000000000011001
// ----------------------------------
// z:00000000000000000000000000111101
C# の XOR 演算
C# での XOR 演算子は ^ です。
XOR 演算では二つのビット列で桁ごとにビット値を比較して、いずれかが 1 であれば 1、 両方とも同じ値の場合に 0 とします。
int x = 0b101100;
int y = 0b011001;
int z = x ^ y;
Console.WriteLine($"x:{Bin(x)}");
Console.WriteLine($"y:{Bin(y)}");
Console.WriteLine("".PadLeft(34, '-'));
Console.WriteLine($"z:{Bin(z)}");
// x:00000000000000000000000000101100
// y:00000000000000000000000000011001
// ----------------------------------
// z:00000000000000000000000000110101
XOR 演算は、特にフラグをゼロクリアする場合などによく使われます。同じフラグに対して XOR 演算を行えば常に 0 になります。
int x = 0b101100;
int z = x ^ x;
Console.WriteLine($"x:{Bin(x)}");
Console.WriteLine("".PadLeft(34, '-'));
Console.WriteLine($"z:{Bin(z)}");
// x:00000000000000000000000000101100
// ----------------------------------
// z:00000000000000000000000000000000
C# の AND 演算
C# での AND 演算子は & です。
AND 演算では二つのビット列で桁ごとにビット値を比較して、両方が 1 の場合に 1 とし、 それ以外は 0 とします。
int x = 0b101110;
int y = 0b011011;
int z = x & y;
Console.WriteLine($"x:{Bin(x)}");
Console.WriteLine($"y:{Bin(y)}");
Console.WriteLine("".PadLeft(34, '-'));
Console.WriteLine($"z:{Bin(z)}");
// x:00000000000000000000000000101110
// y:00000000000000000000000000011011
// ----------------------------------
// z:00000000000000000000000000001010
C# のシフト演算
C# の左シフト演算子は <<、 右シフト演算子は >> です。
変数 演算子 シフト数 (例: x << 3) の形式でシフトする数を指定し、ビット列を右または左にシフトします。
int x = 0b1010100;
int y = x << 3;
Console.WriteLine($"x:{Bin(x)}");
Console.WriteLine("".PadLeft(34, '-'));
Console.WriteLine($"y:{Bin(y)}");
// x:00000000000000000000000001010100
// ----------------------------------
// y:00000000000000000000001010100000
シフト元が存在しない場合はゼロで埋められます。
上の例では左に 3 ビットシフトしています。シフト後の右端 3 ビットはゼロで埋められています。
シフト演算は 2 の累乗数で掛け算 (左シフトの場合) または割り算 (右シフトの場合) を行う場合などに使われます。
左に 1 ビットシフトすると 2 を掛け算した数値になります。 n ビットシフトすると 2 の n 乗を掛け算した数値になります。
int x = 3, y = 0;
Console.WriteLine($"{x,2}: {Bin(x)}");
y = x << 1; // 3 * 2^1
Console.WriteLine($"{y,2}: {Bin(y)}");
y = y << 1; // 3 * 2^2
Console.WriteLine($"{y,2}: {Bin(y)}");
y = y << 1; // 3 * 2^3
Console.WriteLine($"{y,2}: {Bin(y)}");
// 3: 00000000000000000000000000000011
// 6: 00000000000000000000000000000110
//12: 00000000000000000000000000001100
//24: 00000000000000000000000000011000
同様に右に n ビットシフトすると、2 の n 乗で割り算した数値になります。