2

Here is a Java program that left shifts the short value of 2. I have two questions:

Why does num << i for values of i between 16 and 29 produce valid results? A short should only hold values from -32,768 to 32,767.

So I thought, maybe System.out.println interprets num << i as a value larger than a short. But then why does it start producing unexpected values at num << 30?

public class LeftShift {

    public static void main(String[] args)
    {
        short num = 2;
        for (int i = 0; i < 32; i++) {
            System.out.println("Left-shifting 2 by " + i + " places yields: " 
                                + (num << i));
        }
    }

}

The output:

Left-shifting 2 by 0 places yields: 2
Left-shifting 2 by 1 places yields: 4
Left-shifting 2 by 2 places yields: 8
Left-shifting 2 by 3 places yields: 16
Left-shifting 2 by 4 places yields: 32
Left-shifting 2 by 5 places yields: 64
Left-shifting 2 by 6 places yields: 128
Left-shifting 2 by 7 places yields: 256
Left-shifting 2 by 8 places yields: 512
Left-shifting 2 by 9 places yields: 1024
Left-shifting 2 by 10 places yields: 2048
Left-shifting 2 by 11 places yields: 4096
Left-shifting 2 by 12 places yields: 8192
Left-shifting 2 by 13 places yields: 16384
Left-shifting 2 by 14 places yields: 32768
Left-shifting 2 by 15 places yields: 65536
Left-shifting 2 by 16 places yields: 131072
Left-shifting 2 by 17 places yields: 262144
Left-shifting 2 by 18 places yields: 524288
Left-shifting 2 by 19 places yields: 1048576
Left-shifting 2 by 20 places yields: 2097152
Left-shifting 2 by 21 places yields: 4194304
Left-shifting 2 by 22 places yields: 8388608
Left-shifting 2 by 23 places yields: 16777216
Left-shifting 2 by 24 places yields: 33554432
Left-shifting 2 by 25 places yields: 67108864
Left-shifting 2 by 26 places yields: 134217728
Left-shifting 2 by 27 places yields: 268435456
Left-shifting 2 by 28 places yields: 536870912
Left-shifting 2 by 29 places yields: 1073741824
Left-shifting 2 by 30 places yields: -2147483648
Left-shifting 2 by 31 places yields: 0
InvalidBrainException
  • 2,312
  • 8
  • 32
  • 41

6 Answers6

3

Because arithmetic operations on shorts yield ints, unless you cast them back down. For example,

short value = 2;
short result = value << 31;

gives you a compile error that you need to cast it down to a short.

This is for a couple reasons, most notably

  • the bytecode language doesn't really deal with any types smaller than int
  • otherwise operations on shorts overflow much more often.
  • to better model most hardware, which usually doesn't do operations on sub-32-bit values
Louis Wasserman
  • 191,574
  • 25
  • 345
  • 413
3

From the Java language specification, 15.19: "Shift operators"

The type of the shift expression is the promoted type of the left-hand operand.

The promoted type is defined by 5.6: "Numeric promotions":

Otherwise, if the operand is of compile-time type byte, short, or char, unary numeric promotion promotes it to a value of type int by a widening conversion (§5.1.2).

Andy Thomas
  • 84,978
  • 11
  • 107
  • 151
  • 1
    A more relevant quote from 15.19 is "Unary numeric promotion (§5.6.1) is performed on each operand separately." – Louis Wasserman May 29 '12 at 13:24
  • By golly, I swear none of the bit-shifting tutorials I've read on the internet mentioned this. Do you experts read from cover to cover the specification of every language you use? – InvalidBrainException May 29 '12 at 13:26
  • 2
    No, but whenever you spend any time with `short`s, you notice pretty quickly that it requires repeatedly downcasting it back to a `short`. (That said, when you spend lots of time on _StackOverflow,_ you learn that citing the JLS gets you upvotes, since it's basically as definitive as you can get. So even if we didn't learn the answer in the JLS, we'll find the citation to use in an SO answer.) – Louis Wasserman May 29 '12 at 13:48
  • 1
    Also, FYI, this isn't limited to bit-shifting -- it affects _all_ arithmetic on `byte` and `short`. – Louis Wasserman May 29 '12 at 13:53
  • @LouisWasserman - That is also relevant, but the question is about the type of the expression. Another motive for citing the JLS or other original sources is improving the quality of answers on StackOverflow. – Andy Thomas May 29 '12 at 14:24
  • (Sorry, to clarify: I was attempting to explain why it might not be in "a bit-shifting tutorial," since it's a more general thing than bit-shifting.) – Louis Wasserman May 29 '12 at 14:53
3

Note that compound assignment operators will cast the result to the assigned type. So, modifying your example:

public static void main(String[] args) {
    for (int i = 0; i < 32; i++) {
        short num = 2;
        num <<= i; // equivalent to num = (short)(num << i);
        System.out.println("Left-shifting 2 by " + i + " places yields: " + num);
    }
}

This will yield different results, maybe more what you were expecting.

Nathan Ryan
  • 12,893
  • 4
  • 26
  • 37
  • And so I learn another new thing today. I was always taught that compound assignment operators are the same as regular assignment operators. Turns out that they're not! – InvalidBrainException May 29 '12 at 13:37
2

The unexpected value for 30 is an integer overflow, the MSB becomes 1 - and thus is interpeted as a negative number.

Also note that the operator << is yielding ints (unless the argument is a long, then it yields a long). (In fact, the short is being widened to an int first, and only then the << is evaluated.) So the result is as expected - an int, not a short.

amit
  • 175,853
  • 27
  • 231
  • 333
1

What is happening is automatic promotion of operands. In Java, the result of the (num << i) expression in your example is of type int. This is why the result is in the range of an int and not a short.

If you tried to assign (num << i) back into a short, you'd get a compile error, forcing you to cast back to a short.

Peter
  • 6,354
  • 1
  • 31
  • 29
1

See this SO thread.

The part relevant to this question is this quote from here:

If an integer operator has an operand of type long, then the other operand is also
converted to type long. Otherwise the operation is performed on operands of type int, if  
necessary shorter operands are converted into int.

I think the idea is that these widening conversions don't lose information, and could potentially protect you in the case of an overflow. See Chapter 5 of the Java language specification to learn more about widening and narrowing conversions.

Community
  • 1
  • 1
dshapiro
  • 1,075
  • 14
  • 24
  • So is it safe to remember that EVERY assignment operation converts the right-hand side to a short? From looking at that thread, I suppose this applies to C# too? – InvalidBrainException May 29 '12 at 13:57
  • I have a reference book at home that will answer your first question. I'll look it up tonight and post the answer here (if someone doesn't answer it first). As for your second question, I know nothing about C# :) – dshapiro May 29 '12 at 14:38
  • 1
    [Here](http://docs.oracle.com/javase/specs/jls/se5.0/html/conversions.html#26917) is a wonderful description of what's going on (specifically, read section 5.6.1). You've got `short << int`. From the link, we see that unary numeric promotion occurs on each operand of the `<<` operator separately (last bullet point in second bulleted list). Also from the link, we see that "if the operand is of compile-time type byte, short, or char, unary numeric promotion promotes it to a value of type int by a widening conversion". Thus, `short << int` becomes `int << int`, which outputs an `int`. – dshapiro May 30 '12 at 00:42
  • So, to actually answer your question, read the spec on unary numeric promotion and binary numeric promotion to truly understand which kinds of variables get implicitly cast when. – dshapiro May 30 '12 at 00:48