为什么移位运算符似乎循环64位整数?

时间:2017-10-06 16:22:39

标签: c++ bit-manipulation bit-shift

我正在尝试使用C ++中的位数组数据结构。这是一种简单的好奇心,但我该如何解释:

uint64_t a = 1;
uint64_t b = a << 1;

cout << (a == (a << 64)) << endl; // get 1
cout << (a == (b << 63)) << endl; // get 0

似乎是&lt;&lt; x >= 64时x为循环x < 64以零填充。我错了吗?

如果没有,解释是什么?我认为64位整数不是自然周期性的。

2 个答案:

答案 0 :(得分:5)

根据[expr.shift]:

  

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

因此,这是未定义的行为:

uint64_t a = 1;
a << 64;

答案 1 :(得分:1)

正如YSC所解释的那样,在一个操作中移动超过类型大小的是未定义的行为;这个规则来自于将位移操作符直接映射到机器代码指令的愿望,这些指令在这种情况下具有不同的行为,这取决于处理器。

例如,在x86上,SHL指令屏蔽移位量为63(当在64位寄存器上操作时),这可能是您看到a<<64保持为1的原因(64 & 63 == 0因此,它实际上是一种无操作。

请注意,这只是一个教学解释,通常适用于简单情况(通常,禁用优化,或启用优化但移位量未知,因此当移位映射到底层平台移位操作码时)。当使用常量移动常量时,编译器可以传播值并以更高的精度在内部执行算术,或者,即使在一般情况下,也会发出在大于数据类型的寄存器中工作的代码并在最后截断(例如,它是合法的将uint32_t的移位映射到完整的64位寄存器移位,虽然不是特别聪明),从而在这些不合规格的情况下给出不同的结果。请记住:未定义的行为是未定义的,您无法真正期待任何特定的事情发生。

另一方面,按两个步骤执行操作可以正常工作,因为两个操作都是明确定义的(它们在右边填充零,丢弃左边的位)。