How to Use Bitwise Shift Operators in C

Bitwise shift operations in C# allow developers to manipulate individual bits within integral data types by moving them to the left or right. This article covers the mechanics of the left-shift (<<), right-shift (>>), and unsigned right-shift (>>>) operators, explains how integer promotion and count masking affect outcomes, and demonstrates practical code examples for common programming scenarios.

The Left Shift Operator (<<)

The left shift operator (<<) shifts the bits of the left-hand operand to the left by the number of positions specified by the right-hand operand. High-order bits outside the range of the result type are discarded, and empty low-order bit positions are filled with zeros.

Mathematically, shifting an integer left by \(n\) positions is equivalent to multiplying that number by \(2^n\), provided no overflow occurs.

int value = 5;       // Binary: 0000 0101
int result = value << 2; // Binary: 0001 0100 (Decimal: 20)

The Right Shift Operator (>>)

The right shift operator (>>) shifts the bits of the first operand to the right. The behavior of the high-order bits depends on the type of the value being shifted:

  • Unsigned Types (uint, ulong): Performs a logical shift, filling empty high-order positions with zeros.
  • Signed Types (int, long): Performs an arithmetic shift, preserving the sign bit. If the number is positive, empty positions are filled with zeros; if negative, empty positions are filled with ones.

Shifting a value right by \(n\) positions effectively divides the value by \(2^n\), rounded down toward negative infinity.

int positive = 20;     // Binary: 0001 0100
int posResult = positive >> 2; // Binary: 0000 0101 (Decimal: 5)

int negative = -20;    
int negResult = negative >> 2; // Decimal: -5

The Unsigned Right Shift Operator (>>>)

Introduced in C# 11 (.NET 7), the unsigned right shift operator (>>>) performs a logical right shift regardless of whether the operand is signed or unsigned. It always fills the vacated high-order bit positions with zeros.

int negative = -20;
int result = negative >>> 2; 

// Using >> preserves the sign bit (results in a negative number).
// Using >>> inserts leading zeros (results in a large positive number).

Type Promotion and Supported Types

Bitwise shift operators are predefined only for int, uint, long, and ulong.

When applying shift operators to smaller integral types like byte, sbyte, short, or ushort, the runtime automatically promotes them to int before evaluating the expression. Consequently, the result must be explicitly cast back to the smaller type if needed:

byte b = 0b_0000_1100;
byte shifted = (byte)(b << 2);

Shift Count Masking

C# limits the effective shift count to prevent unnecessary or undefined processor cycles. The shift count is masked using only the lowest-order bits:

  • For 32-bit types (int, uint), only the low-order 5 bits of the count are used (count & 0x1F). The effective shift count ranges from 0 to 31.
  • For 64-bit types (long, ulong), only the low-order 6 bits are used (count & 0x3F). The effective shift count ranges from 0 to 63.

Attempting to shift an int by 32 does not clear all bits; instead, value << 32 evaluates as value << 0, returning the original value.

int number = 10;
int shifted = number << 32; // Equivalent to number << 0; output is 10

Compound Assignment Operators

C# provides shorthand compound assignment operators for all shift operations:

  • <<= (Left shift assignment)
  • >>= (Right shift assignment)
  • >>>= (Unsigned right shift assignment)
int mask = 1;
mask <<= 4; // mask is now 16 (0b_0001_0000)

Common Applications

Bitwise shifts are essential for low-level programming tasks:

  • Bit Masking and Flags: Setting, clearing, or reading individual bits within a status register or enum.
  • Data Serialization: Packing multiple smaller values into a single integer, or unpacking bytes from network streams.
  • Performance Optimization: Replacing expensive multiplication or division operations with single-cycle bit shifts when working with powers of two.