[英]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;
}
现在,任何自行右移的数字都将导致0 ( n/(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段说( 强调我的意思 ):
操作数应为整数或无作用域枚举类型,并执行整数提升。 结果的类型是提升后的左操作数的类型。 如果右操作数为负或大于或等于提升后的左操作数的位长度,则该行为是不确定的。
有趣的是,如果移位量为文字,那么gcc
和clang
可能为此代码生成警告:
cout << (b>> 100000) ;
或如果b
是const ,则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.