Confused by undefined C++ shift operator behavior

2019-02-26 02:50发布

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...

3条回答
Juvenile、少年°
2楼-- · 2019-02-26 03:17

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 is 0. Trying the operation on real x86 hardware, you would get 1.

查看更多
你好瞎i
3楼-- · 2019-02-26 03:17

According to the C++ specification if you shift a 32-bit value left by 32, the result is always 0

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.

查看更多
Lonely孤独者°
4楼-- · 2019-02-26 03:22

The type of the result is that of the promoted left operand. The behavior is undefined if the right operand is negative, or greater than or equal to the length in bits of the promoted left operand.

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).

查看更多
登录 后发表回答