Confused by undefined C++ shift operator behavior and wrapping “pattern space”

早过忘川 提交于 2019-12-20 02:51:52

问题


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


回答1:


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.




回答2:


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




回答3:


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.



来源:https://stackoverflow.com/questions/13087816/confused-by-undefined-c-shift-operator-behavior-and-wrapping-pattern-space

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!