6

This is sort of driving me crazy.

int a = 0xffffffff;
int b = 32;
cout << (a << b) << "\n";
cout << (0xffffffff << 32) << "\n";

My output is

-1
0

Why am I not getting

0
0
newprogrammer
  • 2,514
  • 2
  • 28
  • 46
  • http://stackoverflow.com/questions/6916974/change-a-bit-of-an-integer I love ^^^ this post so much i have is as a favourites link. Hope this clarifies bit shifting stuff. It can be very confusing. – Sellorio Apr 18 '13 at 02:06
  • The first one doesn't work because `a` is a signed integer, which can only use 31 of its bits to represent the number. The second one works because the type of `0xffffffff` is promoted to `unsigned int` which uses all 32 bits. Shifting more than the allotted bits for a particular `signed` type is undefined behavior while for the hex literal which is `unsigned`, the behavior is defined and it works as expected. – David G Apr 18 '13 at 15:43
  • Mr Universe's link is not relevant to this question. 0x499602D2's comment is almost completely wrong ... see my answer, which s/he apparently didn't read. – Jim Balter Aug 17 '18 at 06:08

3 Answers3

10

Undefined behavior occurs when you shift a value by a number of bits which is not less than its size (e.g, 32 or more bits for a 32-bit integer). You've just encountered an example of that undefined behavior.

10

The short answer is that, since you're using an implementation with 32-bit int, the language standard says that a 32-bit shift is undefined behavior. Both the C standard (section 6.5.7) and the C++ standard (section 5.8) say

If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined.

But if you want to know why:

Many computers have an instruction that can shift the value in a register, but the hardware only handles shift values that are actually needed. For instance, when shifting a 32 bit word, only 5 bits are needed to represent a shift value of 0 ... 31 and so the hardware may ignore higher order bits, and does on *86 machines (except for the 8086). So that compiler implementations could just use the instruction without generating extra code to check whether the shift value is too big, the authors of the C Standard (many of whom represented compiler vendors) ruled that the result of shifting by larger amounts is undefined.

Your first shift is performed at run time and it encounters this situation ... only the low order 5 bits of b are considered by your machine, and they are 0, so no shift happens. Your second shift is done at compile time, and the compiler calculates the value differently and actually does the 32-bit shift.

If you want to shift by an amount that may be larger than the number of bits in the thing you're shifting, you need to check the range of the value yourself. One possible way to do that is

#define LEFT_SHIFT(a, b) ((b) >= CHAR_BIT * sizeof(a)? 0 : (a) << (b))
Jim Balter
  • 16,163
  • 3
  • 43
  • 66
  • +1 for the distinction between the compile-time and run-time evaluation of the expressions. – Jonathan Leffler Apr 18 '13 at 02:24
  • @JonathanLeffler Of course the compiler *could* have done both calculations at compile time, given the OP's code, but apparently it didn't. – Jim Balter Apr 18 '13 at 02:27
  • Nice answer. I always appreciate it when folks go beyond the minimum, and delve under the hood. Thanks. – Mordachai Apr 18 '13 at 02:29
  • +1. It would be cool, though, to add the quote from the Standard (5.8/2) and point out that this is the case only for signed types. – jogojapan Apr 18 '13 at 02:39
  • @jogojapan It's not the case only for signed types ... that wouldn't make sense given my explanation. The correct section of the C standard is 6.5.7 -- "If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined." – Jim Balter Apr 18 '13 at 02:53
  • @JimBalter I was talking about the C++ Standard. 5.8/2 clearly says that it's defined for unsigned types (using modulo). – jogojapan Apr 18 '13 at 02:55
  • @jogojapan I think you're confusing right shifts with truncated shift amounts ... I just checked the C++ standard and indeed you are; the language I quoted is from the previous paragraph and applies to all integral types. – Jim Balter Apr 18 '13 at 02:57
  • @JimBalter Oops you are right, it's undefined for unsigned in C++, too, because of 5.8/1. Anyway, adding the quote from the Standard would really help. – jogojapan Apr 18 '13 at 02:58
  • IIRC, the 8086 shifts with microcode without truncating the bits in CL. – Aki Suihkonen Apr 18 '13 at 05:34
  • @AkiSuihkonen You're right, and I've made an edit. All later models do truncate, though ... see http://david.wragg.org/blog/2012/11/shift-instructions.html – Jim Balter Apr 18 '13 at 05:59
0

C++ standard says :: If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined. As GCC has no options to handle shifts by negative amounts or by amounts outside the width of the type predictably or trap on them; they are always treated as undefined. So behavior is not defined.

anshul garg
  • 473
  • 3
  • 7