Pop Quiz: Java Shift Operators

| 5 Comments

I've been brushing up on some of the more obscure corners of Java. Here's a pop quiz:

1 << 32 evaluates to:

  1. 0x100000000
  2. 0
  3. 1

Answers below...

A. 0x100000000

This value expressed in binary is 1 followed by 32 zeros. But this is the wrong answer. If you shift a byte, short, char, or int the result value is an int, and this value requires 33 bits, so it is a long. If the expression in question had been 1L << 32 this answer would be correct.

B. 0

Zero is what we get if we take the long value above and truncate it to fit in an int. Seems logical: the expression should shift 32 zero bits into the result, producing an int of all zeros. But this is wrong, too.

C. 1

This is the right answer.

The Java Language Specification specifies that the right-hand operand of the shift operators is masked so that its value is restricted to the range of meaningful values. If the left operand is a byte, short, char, then it is first promoted to an int. If the promoted left operand is an int, then all but the low 5 bits of the right operand are discarded. If the left operand is a long, then all but the low 6 bits of the right operand are discarded.

Our right operand was 32, or 00100000. When we discard all but the low 5 bits we're left with zero. So 1 << 32 is the same as 1 << 0.

I think I knew this at one time, but I would have chosen answer B.

Update

In response to a comment about Java violating the principle of least astonishment, I realized that I didn't know what the answer to this quiz was for C. I assume that Java inherits the behavior from C. I haven't written C in years, but I managed to crank out this program

int main(char **args, int argc) {
  int bits = 32;
  printf("%d\n", 1 << 32);
  printf("%d\n", 1 << bits);
}

The compiler (gcc) issues a warning on the line containing 1 << 32.

When I run it, it prints:

0
1

So, when the compiler can tell that the shift amount is too big, it gives a warning and the expression evaluates to zero. But when the compiler can't tell, it generates code that has the same behavior as C. I think Java ought to issue the warning, but having different behavior in the two cases seems like a bad thing!

5 Comments

I think this is a case where Java violates the principle of least astonishment.

David,

Funny you should mention this; my brother and I based a puzzler on it last year. What does the following program print out:

public class Shifty {
public static void main(String[] args) {
int distance = 0;
while ((-1 << distance) != 0)
distance++;
System.out.println(distance);
}
}

Don't code like my brother,

Click

P.S. Sorry about the lack of formatting. I can't get html tag to work.

ARM has a novel variation on surprises in this area (at least bag in ye olde PROG26 days). MOV r0, #1 / MOV r1, #32 / MOV r0, r0, LSL r1 resulted in r0 as #0. Replace #32 with #256 and r0 would remain #1.

In C the behaviour is undefined when the shift distance is >= the width of the left hand operand (and when the shift distance is negative)

Hi David,

Your quiz is excellent. But in the gcc version 3.3.5 the program displays 1 for both printf.

Books

Comprehensive coverage of Ruby 1.8 and 1.9

"The New Most Important Ruby Book"
Peter Cooper,
rubyinside.com

Completely updated for Ajax and Web 2.0

"A must-have reference"
Brendan Eich,
creator of JavaScript

The classic Java quick-reference