ビットごとの演算子とシフト演算子 (C# リファレンス)

ビット演算子とシフト演算子には、単項ビット補数、2 項左シフトおよび右シフト、符号なし右シフト、2 項論理 AND、OR、排他的 OR 演算子が含まれます。 これらのオペランドは整数の数値型または char 型のオペランドを受け取ります。

これらの演算子は、intuintlongulong 型に対して定義されています。 両方のオペランドが他の整数型 (sbytebyteshortushortchar) の場合、それらの値は int 型に変換され、演算の結果もその型になります。 オペランドが異なる整数型の場合、それらの値は最も近い含んでいる整数型に変換されます。 詳しくは、「C# 言語仕様」の「数値の上位変換」セクションをご覧ください。 複合演算子 (例: >>=) では、引数を int に変換することも、結果の型として int が与えられることもありません。

&|^ の各演算子は、bool 型のオペランドに対しても定義されています。 詳しくは、「ブール論理演算子」をご覧ください。

ビットごとの演算子およびシフト演算が原因でオーバーフローが発生することはなく、checked と unchecked のコンテキストで同じ結果が生成されることはありません。

ビットごとの補数演算子 ~

~ 演算子では、各ビットを反転させることにより、オペランドのビットごとの補数が生成されます。

uint a = 0b_0000_1111_0000_1111_0000_1111_0000_1100;
uint b = ~a;
Console.WriteLine(Convert.ToString(b, toBase: 2));
// Output:
// 11110000111100001111000011110011

~ シンボルはファイナライザーの宣言にも使用できます。 詳細については、「Finalizers」 (ファイナライザー) を参照してください。

左シフト演算子 <<

<< 演算子では、左側のオペランドが、右側のオペランドで定義されたビット数だけ左にシフトされます。 右側のオペランドでのシフト数の定義方法については、「シフト演算子のシフト数」セクションを参照してください。

次の例に示すように、左シフト演算子では、結果の型の範囲外にある上位ビットは破棄され、空の下位ビット位置は、ゼロに設定されます。

uint x = 0b_1100_1001_0000_0000_0000_0000_0001_0001;
Console.WriteLine($"Before: {Convert.ToString(x, toBase: 2)}");

uint y = x << 4;
Console.WriteLine($"After:  {Convert.ToString(y, toBase: 2)}");
// Output:
// Before: 11001001000000000000000000010001
// After:  10010000000000000000000100010000

シフト演算子は intuintlongulong 型に対してのみ定義されるので、演算の結果には常に少なくとも 32 ビットが含まれます。 左側のオペランドが別の整数型 (sbytebyteshortushortchar) の場合、次の例で示すように、その値は int 型に変換されます。

byte a = 0b_1111_0001;

var b = a << 8;
Console.WriteLine(b.GetType());
Console.WriteLine($"Shifted byte: {Convert.ToString(b, toBase: 2)}");
// Output:
// System.Int32
// Shifted byte: 1111000100000000

右シフト演算子 >>

>> 演算子では、左側のオペランドが、右側のオペランドで定義されたビット数だけ右にシフトされます。 右側のオペランドでのシフト数の定義方法については、「シフト演算子のシフト数」セクションを参照してください。

次の例で示すように、右シフト演算では、下位ビットが破棄されます。

uint x = 0b_1001;
Console.WriteLine($"Before: {Convert.ToString(x, toBase: 2), 4}");

uint y = x >> 2;
Console.WriteLine($"After:  {Convert.ToString(y, toBase: 2).PadLeft(4, '0'), 4}");
// Output:
// Before: 1001
// After:  0010

空の上位ビット位置は、左側のオペランドの型に基づいて次のように設定されます。

  • 左側のオペランドの型が int または long である場合、右シフト演算子では、"算術" シフトが実行されます: 左側のオペランドの最上位ビット (符号ビット) の値が空の上位ビット位置に反映されます。 つまり、左側のオペランドが負でない場合は空の上位ビット位置が 0 に設定され、負の場合は 1 に設定されます。

    int a = int.MinValue;
    Console.WriteLine($"Before: {Convert.ToString(a, toBase: 2)}");
    
    int b = a >> 3;
    Console.WriteLine($"After:  {Convert.ToString(b, toBase: 2)}");
    // Output:
    // Before: 10000000000000000000000000000000
    // After:  11110000000000000000000000000000
    
  • 左側のオペランドの型が uint または ulong である場合、右シフト演算子では、"論理" シフトが実行されます: 空の上位ビット位置は常に 0 に設定されます。

    uint c = 0b_1000_0000_0000_0000_0000_0000_0000_0000;
    Console.WriteLine($"Before: {Convert.ToString(c, toBase: 2), 32}");
    
    uint d = c >> 3;
    Console.WriteLine($"After:  {Convert.ToString(d, toBase: 2).PadLeft(32, '0'), 32}");
    // Output:
    // Before: 10000000000000000000000000000000
    // After:  00010000000000000000000000000000
    

Note

符号なし右シフト演算子を使用し、符号付き整数型のオペランドで論理シフトを実行します。 これは、左側のオペランドを符号なし型にキャストし、シフト操作の結果を符号付き型にキャストする場合に推奨されます。

符号なし右シフト演算子 >>>

C# 11 以降で利用できますが、>>> 演算子では、左側のオペランドが、右側のオペランドで定義されたビット数だけ右にシフトされます。 右側のオペランドでのシフト数の定義方法については、「シフト演算子のシフト数」セクションを参照してください。

>>> 演算子では常に "論理" シフトが実行されます。 つまり、左側のオペランドの種類に関係なく、上位の空のビット位置が常に 0 に設定されます。 >> 演算子では、左側のオペランドが符号付き型の場合、"算術" シフトが実行されます (つまり、最上位ビットの値が上位の空のビット位置に反映されます)。 次の例は、左側のオペランドが負の場合の >> 演算子と >>> 演算子の違いを示しています。

int x = -8;
Console.WriteLine($"Before:    {x,11}, hex: {x,8:x}, binary: {Convert.ToString(x, toBase: 2), 32}");

int y = x >> 2;
Console.WriteLine($"After  >>: {y,11}, hex: {y,8:x}, binary: {Convert.ToString(y, toBase: 2), 32}");

int z = x >>> 2;
Console.WriteLine($"After >>>: {z,11}, hex: {z,8:x}, binary: {Convert.ToString(z, toBase: 2).PadLeft(32, '0'), 32}");
// Output:
// Before:             -8, hex: fffffff8, binary: 11111111111111111111111111111000
// After  >>:          -2, hex: fffffffe, binary: 11111111111111111111111111111110
// After >>>:  1073741822, hex: 3ffffffe, binary: 00111111111111111111111111111110

論理 AND 演算子 &

& 演算子によって、その整数オペランドのビットごとの論理 AND が計算されます。

uint a = 0b_1111_1000;
uint b = 0b_1001_1101;
uint c = a & b;
Console.WriteLine(Convert.ToString(c, toBase: 2));
// Output:
// 10011000

bool オペランドの場合、& 演算子がそのオペランドの論理 AND を計算します。 単項 & 演算子はアドレス演算子です。

論理排他的 OR 演算子: ^

^ 演算子によって、その整数オペランドのビットごとの論理排他的 OR (ビットごとの論理 XOR とも呼ばれます) が計算されます。

uint a = 0b_1111_1000;
uint b = 0b_0001_1100;
uint c = a ^ b;
Console.WriteLine(Convert.ToString(c, toBase: 2));
// Output:
// 11100100

bool オペランドの場合、^ 演算子がそのオペランドの論理排他的 OR を計算します。

論理 OR 演算子 |

| 演算子によって、その整数オペランドのビットごとの論理 OR が計算されます。

uint a = 0b_1010_0000;
uint b = 0b_1001_0001;
uint c = a | b;
Console.WriteLine(Convert.ToString(c, toBase: 2));
// Output:
// 10110001

bool オペランドの場合、| 演算子がそのオペランドの論理 OR を計算します。

複合代入。

2 項演算子 op の場合、フォームの複合代入式

x op= y

上記の式は、次の式と同じです。

x = x op y

ただし、x が評価されるのは 1 回だけです。

次の例では、ビットごとの演算子およびシフト演算子を使った複合代入の使用方法を示します。

uint INITIAL_VALUE = 0b_1111_1000;

uint a = INITIAL_VALUE;
a &= 0b_1001_1101; 
Display(a);  // output: 10011000

a = INITIAL_VALUE;
a |= 0b_0011_0001; 
Display(a);  // output: 11111001

a = INITIAL_VALUE;
a ^= 0b_1000_0000;
Display(a);  // output: 01111000

a = INITIAL_VALUE;
a <<= 2;
Display(a);  // output: 1111100000

a = INITIAL_VALUE;
a >>= 4;
Display(a);  // output: 00001111

a = INITIAL_VALUE;
a >>>= 4;
Display(a);  // output: 00001111

void Display(uint x) => Console.WriteLine($"{Convert.ToString(x, toBase: 2).PadLeft(8, '0'), 8}");

数値の上位変換のため、op 演算の結果は、x の型 T に暗黙的に変換できない可能性があります。 そのような場合、op が定義済みの演算子であり、演算の結果が x の型 T に明示的に変換できる場合、x op= y の形式の複合代入式は、x が 1 回だけ評価される点を除き、x = (T)(x op y) と等価です。 次の例は、その動作を示します。

byte x = 0b_1111_0001;

int b = x << 8;
Console.WriteLine($"{Convert.ToString(b, toBase: 2)}");  // output: 1111000100000000

x <<= 8;
Console.WriteLine(x);  // output: 0

演算子の優先順位

次のビットごとの演算子およびシフト演算子の一覧は、優先度が高い順に並べられています。

  • ビットごとの補数演算子 ~
  • シフト演算子 <<>>>>>
  • 論理 AND 演算子 &
  • 論理排他的 OR 演算子 ^
  • 論理 OR 演算子 |

演算子の優先順位によって定められた評価の順序を変更するには、かっこ () を使用します。

uint a = 0b_1101;
uint b = 0b_1001;
uint c = 0b_1010;

uint d1 = a | b & c;
Display(d1);  // output: 1101

uint d2 = (a | b) & c;
Display(d2);  // output: 1000

void Display(uint x) => Console.WriteLine($"{Convert.ToString(x, toBase: 2), 4}");

優先度順に並べられた C# 演算子の完全な一覧については、C# 演算子に関する記事の「演算子の優先順位」セクションを参照してください。

シフト演算子のシフト数

組み込みシフト演算子 <<>>>>> の場合、右側のオペランドの型は int であるか、int事前に定義された暗黙的な数値変換を持つ型にする必要があります。

x << countx >> countx >>> count の式では、実際のシフト数は次のように x の型によって異なります。

  • x の型が int または uint である場合、シフト数は、右側のオペランドの下位 5 ビットで定義されます。 つまり、シフト数は count & 0x1F (または count & 0b_1_1111) から計算されます。

  • x の型が long または ulong である場合、シフト数は、右側のオペランドの下位 6 ビットで定義されます。 つまり、シフト数は count & 0x3F (または count & 0b_11_1111) から計算されます。

次の例は、その動作を示します。

int count1 = 0b_0000_0001;
int count2 = 0b_1110_0001;

int a = 0b_0001;
Console.WriteLine($"{a} << {count1} is {a << count1}; {a} << {count2} is {a << count2}");
// Output:
// 1 << 1 is 2; 1 << 225 is 2

int b = 0b_0100;
Console.WriteLine($"{b} >> {count1} is {b >> count1}; {b} >> {count2} is {b >> count2}");
// Output:
// 4 >> 1 is 2; 4 >> 225 is 2

int count = -31;
int c = 0b_0001;
Console.WriteLine($"{c} << {count} is {c << count}");
// Output:
// 1 << -31 is 2

注意

前の例で示したように、右側のオペランドの値が左側のオペランドのビット数よりも大きい場合でも、シフト演算の結果が 0 以外になることがあります。

列挙論理演算子

~&|^ の演算子は、任意の列挙型でもサポートされます。 オペランドが同じ列挙型の場合、基になっている整数型の対応する値に対して、論理演算が実行されます。 たとえば、基になる型が U である列挙型 T の任意の xy に対して、式 x & y では式 (T)((U)x & (U)y) と同じ結果が生成されます。

通常、ビットごとの論理演算子は、Flags 属性で定義されている列挙型で使います。 詳しくは、「列挙型」記事の「ビット フラグとしての列挙型」セクションをご覧ください。

演算子のオーバーロード可/不可

ユーザー定義型では、~<<>>>>>&|^ の各演算子をオーバーロードできます。 2 項演算子をオーバーロードすると、対応する複合代入演算子も暗黙的にオーバーロードされます。 ユーザー定義型では、複合代入演算子を明示的にオーバーロードすることはできません。

ユーザー定義型 T<<>>>>> 演算子をオーバーロードする場合、左側のオペランドの型は T である必要があります。 C# 10 以前では、右側のオペランドの型は int である必要があります。C# 11 以降、オーバーロードされたシフト演算子の右側のオペランドの型は任意です。

C# 言語仕様

詳細については、「C# 言語仕様」の次のセクションを参照してください。

関連項目