Bitwise and shift operators (C# Reference)

The following operators perform bitwise or shift operations with operands of the integral types:

Those operators are defined for the int, uint, long, and ulong types. When both operands are of other integral types (sbyte, byte, short, ushort, or char), their values are converted to the int type, which is also the result type of an operation. When operands are of different integral types, their values are converted to the closest containing integral type. For more information, see the Numeric promotions section of the C# language specification.

The &, |, and ^ operators are also defined for the operands of the bool type. For more information, see Boolean logical operators.

Bitwise and shift operations never cause overflow and produce the same results in checked and unchecked contexts.

Bitwise complement operator ~

The ~ operator produces a bitwise complement of its operand by reversing each bit:

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

You can also use the ~ symbol to declare finalizers. For more information, see Finalizers.

Left-shift operator <<

The << operator shifts its first operand left by the number of bits defined by its second operand.

The left-shift operation discards the high-order bits that are outside the range of the result type and sets the low-order empty bit positions to zero, as the following example shows:

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

Because the shift operators are defined only for the int, uint, long, and ulong types, the result of an operation always contains at least 32 bits. If the first operand is of another integral type (sbyte, byte, short, ushort, or char), its value is converted to the int type, as the following example shows:

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

For information about how the second operand of the << operator defines the shift count, see the Shift count of the shift operators section.

Right-shift operator >>

The >> operator shifts its first operand right by the number of bits defined by its second operand.

The right-shift operation discards the low-order bits, as the following example shows:

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), 4}");
// Output:
// Before: 1001
// After:    10

The high-order empty bit positions are set based on the type of the first operand as follows:

  • If the first operand is of type int or long, the right-shift operator performs an arithmetic shift: the value of the most significant bit (the sign bit) of the first operand is propagated to the high-order empty bit positions. That is, the high-order empty bit positions are set to zero if the first operand is non-negative and set to one if it's negative.

    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
    
  • If the first operand is of type uint or ulong, the right-shift operator performs a logical shift: the high-order empty bit positions are always set to zero.

    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), 32}");
    // Output:
    // Before: 10000000000000000000000000000000
    // After:     10000000000000000000000000000
    

For information about how the second operand of the >> operator defines the shift count, see the Shift count of the shift operators section.

Logical AND operator &

The & operator computes the bitwise logical AND of its operands:

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

For the operands of the bool type, the & operator computes the logical AND of its operands. The unary & operator is the address-of operator.

Logical exclusive OR operator ^

The ^ operator computes the bitwise logical exclusive OR, also known as the bitwise logical XOR, of its operands:

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

For the operands of the bool type, the ^ operator computes the logical exclusive OR of its operands.

Logical OR operator |

The | operator computes the bitwise logical OR of its operands:

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

For the operands of the bool type, the | operator computes the logical OR of its operands.

Compound assignment

For a binary operator op, a compound assignment expression of the form

x op= y

is equivalent to

x = x op y

except that x is only evaluated once.

The following example demonstrates the usage of compound assignment with bitwise and shift operators:

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

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

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

a <<= 2;
Display(a);  // output: 11100100

a >>= 4;
Display(a);  // output:     1110

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

Because of numeric promotions, the result of the op operation might be not implicitly convertible to the type T of x. In such a case, if op is a predefined operator and the result of the operation is explicitly convertible to the type T of x, a compound assignment expression of the form x op= y is equivalent to x = (T)(x op y), except that x is only evaluated once. The following example demonstrates that behavior:

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

Operator precedence

The following list orders bitwise and shift operators starting from the highest precedence to the lowest:

  • Bitwise complement operator ~
  • Shift operators << and >>
  • Logical AND operator &
  • Logical exclusive OR operator ^
  • Logical OR operator |

Use parentheses, (), to change the order of evaluation imposed by operator precedence:

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}");

For the complete list of C# operators ordered by precedence level, see C# operators.

Shift count of the shift operators

For the shift operators << and >>, the type of the second operand must be int or a type that has a predefined implicit numeric conversion to int.

For the x << count and x >> count expressions, the actual shift count depends on the type of x as follows:

  • If the type of x is int or uint, the shift count is defined by the low-order five bits of the second operand. That is, the shift count is computed from count & 0x1F (or count & 0b_1_1111).

  • If the type of x is long or ulong, the shift count is defined by the low-order six bits of the second operand. That is, the shift count is computed from count & 0x3F (or count & 0b_11_1111).

The following example demonstrates that behavior:

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}");

int b = 0b_0100;
Console.WriteLine($"{b} >> {count1} is {b >> count1}; {b} >> {count2} is {b >> count2}");

Enumeration logical operators

The ~, &, |, and ^ operators are also defined for any enumeration type. For the operands of the same enumeration type, a logical operation is performed on the corresponding values of the underlying integral type. For example, for any x and y of an enumeration type T with an underlying type U, the x & y expression produces the same result as the (T)((U)x & (U)y) expression.

You typically use bitwise logical operators with an enumeration type which is defined with the Flags attribute. For more information, see the Enumeration types as bit flags section of the Enumeration types article.

Operator overloadability

A user-defined type can overload the ~, <<, >>, &, |, and ^ operators. When a binary operator is overloaded, the corresponding compound assignment operator is also implicitly overloaded. A user-defined type cannot explicitly overload a compound assignment operator.

If a user-defined type T overloads the << or >> operator, the type of the first operand must be T and the type of the second operand must be int.

C# language specification

For more information, see the following sections of the C# language specification:

See also