算术右移给出虚假的结果?

时间:2013-10-28 13:49:28

标签: c++ gcc undefined-behavior bit-shift

我必须在这里绝对疯狂,但我机器上的gcc 4.7.3给出了最荒谬的结果。这是我正在测试的确切代码:

#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积极/无符号),但不知何故,这是我的输出:

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

2 个答案:

答案 0 :(得分:47)

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

实际上,常见的结果是使用了移位量的5个最低有效位,忽略了高阶位;这是因为编译器生成的机器指令完全正确(例如x86上的SHR)。

在这种情况下,移位值为100000(十进制),恰好是二进制的11000011010100000 - 低5位为零。所以,你有效地获得了0.你不应该依赖于此。从技术上讲,你所看到的是未定义的行为

参考文献:

对于C,N1570第6.5.7节:

  

如果右操作数的值为负或大于或   等于提升的左操作数的宽度,行为是   未定义。

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

  

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

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

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

答案 1 :(得分:32)

如果移位大于左操作数的位长,undefined behavior部分5.8 移位运算符 1,则调用draft C++ standard 说(强调我的):

  

操作数应为整数或无范围的枚举类型,并执行整体促销。结果的类型是提升的左操作数的类型。 如果右操作数为负数,或者大于或等于提升左操作数的位长度,则行为未定义。

有趣的是,gccclang 可能会为此代码生成警告,如果文字的移位金额:

cout << (b>> 100000) ;

或如果b const gcc的警告如下:

warning: right shift count >= width of type [enabled by default]

正如MSalters在问题的评论中指出的那样,我们可能无法依赖此警告,因为这是未定义的行为,这与 undefined上的标准说明一致术语和定义部分中的行为,其中包含:

  

注意:[...]允许的未定义行为包括完全忽略不可预测的结果,在翻译或程序执行期间以环境特征的文件化方式行事(有或没有发出诊断消息),终止翻译或执行(发布诊断消息)。 [...]

平台特定详情

示例代码明显缺乏转换的可能解释可能是因为在某些平台上,移位计数将屏蔽5 bits,例如在{{1}上}架构我们可以在 IA-32架构兼容性部分中看到Intel® 64 and IA-32 Architectures Software Developer’s Manual部分 SAL / SAR / SHL / SHR-Shift

  

8086不会掩盖班次计数。但是,所有其他IA-32处理器(从Intel 286处理器开始)确实将移位计数屏蔽为5位,最大计数为31. [...]