算术右移给出虚假结果?

2021-12-18 00:00:00 gcc bit-shift c++ undefined-behavior

我一定是疯了,但是我机器上的 gcc 4.7.3 给出了最荒谬的结果.这是我正在测试的确切代码:

I must be absolutely crazy here, but gcc 4.7.3 on my machine is giving the most absurd result. Here is the exact code that I'm testing:

#include <iostream>

using namespace std;

int main(){
  unsigned int b = 100000;
  cout << (b>>b) << endl;
  b = b >> b;
  cout << b << endl;
  b >>= b;
  cout << b << endl;
  return 0;
}

现在,任何自行右移的数字都应该导致 0 (n/(2^n) == 0 与 整数除法、n>1 和 positive/unsigned),但不知何故这是我的输出:

Now, any number that's right shifted by itself should result in 0 (n/(2^n) == 0 with integer divide, n>1, and positive/unsigned), but somehow here is my output:

100000
100000
100000

我疯了吗?可能会发生什么?

Am I crazy? What could possibly be going on?

推荐答案

在 C++ 中与在 C 中一样,移位仅限于移位值的大小(以位为单位).例如,如果 unsigned int 是 32 位,那么超过 31 位的移位是未定义的.

In C++ as in C, shifts are limited to the size (in bits) of the value shifted. For instance, if unsigned int is 32 bits, then a shift of more than 31 is undefined.

在实践中,一个常见的结果是使用移位量的最低5位,忽略高位;这是因为编译器生成了一条完全符合此要求的机器指令(例如 x86 上的 SHR).

In practice, a common result is that the 5 least-significant bits of the shift amount are used and the higher order bits are ignored; this is due to the compiler producing a machine instruction which does exactly that (eg SHR on x86).

在这种情况下,移位值是 100000(十进制),恰好是二进制的 11000011010100000 - 低 5 位为零.所以,你实际上得到了一个 0 的转变.但是你不应该依赖这个;从技术上讲,您看到的是未定义行为.

In this case, the shift value is 100000 (decimal) which happens to be 11000011010100000 in binary - the lower 5 bits are zero. So, you're effectively getting a shift by 0. You shouldn't rely on this however; technically, what you're seeing is undefined behaviour.

参考文献:

对于 C,N1570 6.5 节.7:

For C, N1570 section 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.

对于 C++,N3690第 5.8 节[expr.shift]":

For C++, N3690 section 5.8 "[expr.shift]":

如果右操作数为负数或更大,则行为未定义大于或等于提升的左操作数的位长度.

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.

N1570 是一个草案,与已发布的 ISO C11 标准几乎相同;该条款自 1989 年 ANSI C 标准以来几乎相同.

N1570 is a draft, nearly identical to the released ISO C11 standard; this clause has been pretty much the same since the 1989 ANSI C standard.

N3690 是 C++ 标准的最新草案;我不确定它是否是最好用的,但同样,这个条款没有改变.

N3690 is a recent draft of the C++ standard; I'm not sure whether it's the best one to use, but again, this clause hasn't changed.

相关文章