7

Consider the following Example:

First Case:

short x=255;
x = (x<<8)>>8;
cout<<x<<endl;

Second Case:

short x=255;
x = x<<8;
x = x>>8;
cout<<x<<endl;

The output in the first case is 255 whereas in the second case is -1. -1 as output does makes sense as cpp does a arithmetic right shift. Here are the intermediate values of x to obtain -1 as output.

x: 0000 0000 1111 1111  
x<<8:1111 1111 0000 0000 
x>>8:1111 1111 1111 1111

Why doesn't the same mechanism happen in the first case?

router
  • 582
  • 5
  • 16
  • @BaummitAugen: Wow, I thought that "1s stretching by right-shift" was dictated by the language standard... Good to know it's not... EDIT: Well, I see that you've removed your previous comment with a link to a related answer... So which one is it? "1s stretching by right-shift" defined by the standard or not? – barak manos Aug 30 '16 at 11:05
  • 1
    @barakmanos The answer I linked (for reference: [here](http://stackoverflow.com/a/4009922/3002139)) is still valid, it just does not apply here as I thought it does (see Sam's answer below). – Baum mit Augen Aug 30 '16 at 11:13

2 Answers2

12

The difference is a result of two factors.

  1. The C++ standard does not specify the maximum values of integral types. The standard only specifies the minimum size of each integer type. On your platform, a short is a 16 bit value, and an ints is at least a 32 bit value.

  2. The second factor is two's complement arithmetic.

In your first example, the short value is naturally promoted to an int, which is at least 32 bits, so the left and the right shift operates on an int, before getting converted back to a short.

In your second example, after the first left shift operation the resulting value is once again converted back to a short, and due to two's complement arithmetic, it ends up being a negative value. The right shift ends up sign-extending the negative value, resulting in the final result of -1.

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
  • strictly speaking, the conversion to "shorter" signed integral is not specified by the standard (it is implementation defined), so theoretically 2nd example may produce arbitrary value. But usually compiler just decides to truncate and we have -1 as the result – user396672 Aug 30 '16 at 12:16
1

What you just observed is sign extension:

Sign extension is the operation, in computer arithmetic, of increasing the number of bits of a binary number while preserving the number's sign (positive/negative) and value. This is done by appending digits to the most significant side of the number, following a procedure dependent on the particular signed number representation used.

For example, if six bits are used to represent the number "00 1010" (decimal positive 10) and the sign extend operation increases the word length to 16 bits, then the new representation is simply "0000 0000 0000 1010". Thus, both the value and the fact that the value was positive are maintained.

If ten bits are used to represent the value "11 1111 0001" (decimal negative 15) using two's complement, and this is sign extended to 16 bits, the new representation is "1111 1111 1111 0001". Thus, by padding the left side with ones, the negative sign and the value of the original number are maintained.

You rigt shift all the way to the point where your short becomes negative, and when you then shift back, you get the sign extension.

This doesn't happen in the first case, as the shift isn't applied to a short. It's applied to 255 which isn't a short, but the default integral type (probably an int). It only gets casted after it's already been shifted back:

on the stack:     0000 0000 0000 0000 0000 0000 1111 1111
<<8
on the stack:     0000 0000 0000 0000 1111 1111 0000 0000
>>8
on the stack:     0000 0000 0000 0000 0000 0000 1111 1111
convert to short: 0000 0000 1111 1111
Community
  • 1
  • 1
Lanting
  • 3,060
  • 12
  • 28
  • 2
    I thought it is worth mentioning that this phenomenon is not guaranteed. Strictly speaking this is *implementation-defined*, which means you have to consult the documentation that comes with your compiler for the target platform. – rustyx Aug 30 '16 at 14:18