I'm confused by something I read in the Shift Operators section of an article on undefined C++ behavior.
On the ARM architecture, the shift operators always behave as if they take place in a 256-bit pattern space, regardless of the operand size--that is, the pattern repeats, or "wraps around", only every 256 positions. Another way of thinking of this is that the pattern is shifted the specified number of positions modulo 256. Then, of course, the result contains just the least-significant bits of the pattern space.
The tables are especially strange:
Given a 32-bit integer with a value of 1:
+-----------------------------------+
| Shift left ARM x86 x64 |
+-----------------------------------+
| 32 0 1 1 |
| 48 0 32768 32768 |
| 64 0 1 1 |
+-----------------------------------+
What are these values, and why do they matter?
The shift operators don't wrap. According to the C++ specification if you shift a 32-bit value left by 32, the result is always 0. (EDIT: I'm wrong, see the answers!) So what is this article getting at? What is the undefined behavior?
When I run this code on x86 I get 0
:
printf("%d", 1 << 32);
Supposedly this code snippet illustrates the problem:
// C4293.cpp
// compile with: /c /W1
unsigned __int64 combine (unsigned lo, unsigned hi) {
return (hi << 32) | lo; // C4293
// try the following line instead
// return ( (unsigned __int64)hi << 32) | lo;
}
I would expect the returned value to be lo
, since the programmer shifted away all the hi
bits. A warning is nice, since this was probably a mistake, but I don't see any undefined behavior...
If you use the x86 or x64 machine instructions for shifting the value, they will mask off the shift amount and only use the lower bits for the actual shift. Some other hardware might not do that.
That's why it is undefined.
In your example with literals
1 << 32
, it is likely that the compiler computes the value and that's why it is0
. Trying the operation on real x86 hardware, you would get1
.No, that's not what the standard says. It is undefined behavior to shift a 32-bit type by 32 or more (5.8/1)
Since it is undefined behavior to shift by 256 bits on ARM (which has no 257-bit-or-greater type), the CPU is perfectly entitled to wrap at that point.
This is from §5.8/1 from C++11. If your ints are 32bit, you can't shift by 32 (right- or left-shift, regardless of the signedness of the left operand).