Shifting bits to the right moves every bit toward the least significant end and drops the bits that fall off. The question is what fills the empty positions on the left. There are two answers, and they give very different results for negative numbers.

The Two Kinds of Right Shift

  • A logical right shift fills the vacated high bits with 0.
  • An arithmetic right shift fills them with copies of the original sign bit (the most significant bit).

For a value whose top bit is 0, both produce the same result. The difference appears only when the top bit is 1:

Input                 11110000   (240 unsigned, −16 signed)

Logical  >> 2         00111100   (60)
Arithmetic >> 2       11111100   (−4 signed, 252 unsigned)

The logical shift treats the value as an unsigned bit pattern. The arithmetic shift treats it as a signed number and keeps it negative.

Why Arithmetic Shift Exists

A right shift by n divides by 2^n. For unsigned values, a logical shift does this correctly: 240 >> 2 = 60. For a negative two’s complement value, zero fill would turn it into a large positive number, which is wrong. Copying the sign bit keeps the result negative, so −16 >> 2 = −4.

This is called sign extension, the same process used when widening a signed integer. The background is in Two’s Complement Explained.

Rounding: Why −7 >> 1 Is −4

Arithmetic right shift rounds toward negative infinity, while integer division in C, Java, and JavaScript rounds toward zero:

ExpressionResult
7 >> 13
7 / 2 (integer division)3
−7 >> 1−4
−7 / 2 in C, Java−3
−7 // 2 in Python−4

So x >> 1 and x / 2 are not interchangeable for negative numbers in C-family languages. Compilers know this and add a small correction when they optimise signed division into a shift.

How Languages Handle Right Shift

LanguageSigned >>Logical shift
C, C++arithmetic on all mainstream compilers (guaranteed since C++20)use an unsigned type
Javaarithmetic>>>
JavaScriptarithmetic on int32>>> (returns uint32)
C#arithmetic>>> (C# 11+) or unsigned types
Pythonarithmetic (floor division)mask first: (x & 0xFFFFFFFF) >> n
Rustarithmetic for i32, logical for u32cast to the unsigned type
Goarithmetic for signed, logical for unsignedconvert to uint

In C, right-shifting a negative signed value is implementation-defined, although every common compiler uses an arithmetic shift. The portable approach is still to use unsigned types whenever you mean “move these bits”.

int32_t  s = -16;
uint32_t u = (uint32_t)s;   /* 0xFFFFFFF0 */
printf("%d\n", s >> 2);     /* -4 */
printf("%u\n", u >> 2);     /* 1073741820 (0x3FFFFFFC) */

Choosing the Right Shift

Use a logical shift when the value is a bit pattern rather than a number:

  • extracting fields from a packed register or protocol header,
  • working with hashes, checksums, and random number generators,
  • unpacking RGB colours.

Use an arithmetic shift when the value is a signed number and you want to scale it:

  • fixed-point arithmetic (for example, converting a Q16.16 value to an integer),
  • audio and signal processing on signed samples,
  • quick division by powers of two where rounding toward negative infinity is acceptable.

A Common Bug

A frequent mistake is extracting a field from a signed value with >>:

int header = 0xF0000000;            // top nibble is 0xF
int top = header >> 28;             // -1, not 15!
int topFixed = header >>> 28;       // 15
int topMasked = (header >> 28) & 0xF; // 15

The arithmetic shift copies the sign bit into every new position, so the result is 0xFFFFFFFF. Either use the logical shift or mask after shifting.

Try It

The Right Shift Calculator shows logical and arithmetic results side by side. Enter 11110000, choose 8-bit width, and shift by 2 to reproduce the example above. For left shifts, which are the same for signed and unsigned values except for overflow rules, use the Left Shift Calculator.