繁体   English   中英

算术右移给出假结果吗?

[英]Arithmetic right shift gives bogus result?

我必须在这里绝对疯了,但是我机器上的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;
}

现在,任何自行右移的数字都将导致0n/(2^n) == 0整数除法n>1正/无符号 ),但是以某种方式这是我的输出:

100000
100000
100000

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

在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 ++标准的最新草案; 我不确定这是否是最好的,但再次,此子句没有改变。

如果移位大于左操作数的位长,则会调用未定义的行为C ++标准草案 5.8 移位运算符1段说( 强调我的意思 ):

操作数应为整数或无作用域枚举类型,并执行整数提升。 结果的类型是提升后的左操作数的类型。 如果右操作数为负或大于或等于提升后的左操作数的位长度,则该行为是不确定的。

有趣的是,如果移位量为文字,那么gccclang 可能为此代码生成警告:

cout << (b>> 100000) ;

或如果bconst ,则gcc的警告如下:

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

正如MSalters在对该问题的评论中指出的那样,我们可能甚至不能依靠此警告,因为这是未定义的行为 ,这与“ 术语和定义”部分中有关未定义的行为的标准注释一致:

注意:[...]允许的不确定行为包括完全忽略具有无法预测结果的情况,以环境特征的书面形式在翻译或程序执行期间的行为(带有或不带有诊断消息),终止转换或执行(带有诊断消息的发布)。 [...]

平台特定的详细信息

对于示例代码中明显缺乏移位的可能解释,可能是因为在某些平台上,移位计数将被掩码5 bits ,例如在x86架构上,我们可以看到《 英特尔®64和IA-32架构软件开发人员手册》 SAL / SAR / SHL / SHR部分-IA -32体系结构兼容性部分中的转换说:

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM