skip to content

Explain Java's three shift operators <<, >>, and >>> and how they differ.

level: middleimportance: must knowfreq 58%

answer

  1. << fills 0 low, *2^n
  2. '>>' sign-extends, floor /2^n
  3. '>>>' zero-fills high, unsigned
  4. no <<< exists
  5. (low+high) >>> 1 = safe midpoint

basics

~20 s

<< moves bits left (filling zeros on the right), multiplying by powers of two. >> moves bits right keeping the sign (negatives stay negative). >>> moves bits right but always fills zeros on the left, ignoring the sign.

solid answer

~50 s

All three move the binary digits of an integer by a given number of positions. Left shift `<<` shifts toward the high bits, filling the vacated low bits with 0; `x << n` equals `x * 2^n` (modulo overflow). Signed right shift `>>` shifts toward the low bits and fills the vacated high bits with copies of the sign bit (sign-extension), so it preserves sign and behaves like floor-dividing by 2^n. Unsigned (logical) right shift `>>>` also shifts right but always fills the high bits with 0, regardless of sign — making the result non-negative for the bit pattern. Java has no `<<<`; left shift never needs sign handling. `>>>` is the tool for treating an int/long as raw unsigned bits, e.g. extracting high bytes, in hashing, or computing a midpoint without overflow: `(low + high) >>> 1`.

code

java · 8 lines
java
System.out.println(3 << 2);    // 12   (3 * 2^2)
System.out.println(-8 >> 1);   // -4   (sign-extended, floor)
System.out.println(-8 >>> 1);  // 2147483644 (zero-filled)
System.out.println(-1 >>> 28); // 15   (low 4 bits remain)

// overflow-safe midpoint
int low = 2_000_000_000, high = 2_000_000_001;
int mid = (low + high) >>> 1; // correct, even though low+high overflows

go deeper

for a junior

Knows << multiplies by powers of two and >> divides, and that there are three shift operators.

for a middle

Clearly distinguishes >> (sign-extending) from >>> (zero-filling) and knows << has no signed/unsigned variant.

for a senior

Explains floor vs truncation for negative >>, the overflow-safe midpoint idiom, and uses >>> for unsigned-bit extraction and hashing.

for a principal

Reasons about shifts in performance-critical/branch-free code and library design (binarySearch, HashMap spread), and teaches the sign-extension pitfalls.

## Shifting = sliding the bits A **shift operator** moves all the bits of an integer left or right by a number of positions. Think of the bits written out, then slid; bits that fall off the end are discarded, and the gap left behind on the other side is filled with new bits. *What* fills the gap is the whole story. ## Left shift `<<` `x << n` slides bits toward the **high** (most-significant) end and fills the freed **low** bits with `0`. Each position is a multiplication by 2, so `x << n == x * 2^n` as long as no significant bits fall off the top (otherwise it overflows and wraps, since Java integer math is modular). Example: `3 << 2` = `0011` → `1100` = 12 = 3 * 4. ## Signed right shift `>>` `x >> n` slides bits toward the **low** end. The freed **high** bits are filled with copies of the **sign bit** (the leftmost bit: 0 for non-negative, 1 for negative). This is **sign extension**, and it keeps the number's sign. For non-negative `x` it equals `x / 2^n`; for negatives it is *floor* division (rounds toward negative infinity), e.g. `-8 >> 1 = -4`, and `-1 >> n` stays `-1` (all ones stays all ones). ## Unsigned / logical right shift `>>>` `x >>> n` also slides right but **always fills the high bits with 0**, ignoring the sign bit. For non-negative numbers it behaves identically to `>>`. For negatives the result becomes a large positive number, because the leading 1s are replaced by 0s. `-1 >>> 28` = 15 (`...1111` becomes `0000...1111`). This is the way to treat a signed type as a bag of **unsigned** bits. There is **no `<<<`** operator: left shift fills with 0 already, so a 'signed' vs 'unsigned' distinction is meaningless on the left. ## Why >>> exists — concrete uses - Extracting a high byte: `(value >>> 24) & 0xFF`. - Overflow-safe midpoint in binary search: `int mid = (low + high) >>> 1;` — even if `low + high` overflows into a negative int, the logical shift gives the correct unsigned average. (`Arrays.binarySearch` uses this.) - Hashing: `h ^ (h >>> 16)` (e.g. `HashMap`) spreads high bits into low bits. ## Type rules Left operand is promoted to `int` if it's `byte`/`short`/`char`; the result type is `int`, or `long` if the left operand is `long`. The shift result follows the **left** operand's type — important for the shift-distance modulo behavior covered separately. ## Common mistakes - Using `>>` on a value you intend to be unsigned (e.g. a byte read into an int as 0xFF) and getting sign extension you didn't want. - Assuming `>>` rounds toward zero — it floors, so `-1 >> 1 == -1`, not 0. - Forgetting overflow on `<<`: shifting a 1 into or past the sign bit produces negative or zero results.

  • Why is (low + high) >>> 1 preferred over (low + high) / 2 in binary search?
    If low + high overflows past Integer.MAX_VALUE it becomes negative; / 2 then gives a wrong negative index, but >>> 1 treats the sum as unsigned bits and yields the correct midpoint.
  • What is -8 >> 2 and -8 >>> 2?
    -8 >> 2 = -2 (sign-extended floor divide). -8 >>> 2 = a large positive number (1073741822), because the leading sign 1s are replaced with zeros.

saying these in an interview costs you the question

  • Saying >> and >>> are the same for negative numbers
  • Claiming there is a <<< operator
  • Thinking >> rounds toward zero (it floors)
  • Believing << can never produce a negative result

context