为什么移位运算符似乎循环 64 位整数?
Why does the shift operator seems to cycle 64 bits integer?
我正在尝试使用 C++ 中的位数组数据结构。单纯的好奇,但我该怎么解释呢:
uint64_t a = 1;
uint64_t b = a << 1;
cout << (a == (a << 64)) << endl; // get 1
cout << (a == (b << 63)) << endl; // get 0
似乎 << x 在 x >= 64
时是 循环 ,但在 x < 64
时 用零填充 .我错了吗?
如果不是,解释是什么?我认为 64 位整数不是自然循环的。
根据[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.
因此,这是未定义的行为:
uint64_t a = 1;
a << 64;
正如 YSC 所解释的,在一次操作中移动 超过类型大小的 是未定义的行为;这条规则来自于将位移运算符直接映射到机器代码指令的愿望,在这种情况下,机器代码指令具有不同的行为,具体取决于处理器。
例如,在 x86 上,SHL 指令用 63 屏蔽移位量(在 64 位寄存器上操作时),这可能是您看到 a<<64
保持为 1 的原因(因为 64 & 63 == 0
,因此它实际上是一个空操作)。
请注意,这只是一个指导性解释,通常适用于简单情况(通常,禁用优化,或启用优化但移位量未知,因此当移位确实映射到底层平台移位操作码时)。当用常量移动常量时,编译器可能会传播值并在内部以更高的精度执行算术运算,或者,即使在一般情况下,发出在大于您的数据类型的寄存器中工作的代码并在末尾截断(例如,合法的是将 uint32_t
的移位映射到完整的 64 位寄存器移位,尽管不是特别聪明),因此在这些超出规格的情况下会给出不同的结果。请记住:未定义的行为是未定义的,您不能真正期望发生任何特定的事情。
另一方面,分两步进行操作按预期工作,因为这两个操作都是明确定义的(它们在右侧填充零,丢弃左侧的位)。
我正在尝试使用 C++ 中的位数组数据结构。单纯的好奇,但我该怎么解释呢:
uint64_t a = 1;
uint64_t b = a << 1;
cout << (a == (a << 64)) << endl; // get 1
cout << (a == (b << 63)) << endl; // get 0
似乎 << x 在 x >= 64
时是 循环 ,但在 x < 64
时 用零填充 .我错了吗?
如果不是,解释是什么?我认为 64 位整数不是自然循环的。
根据[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.
因此,这是未定义的行为:
uint64_t a = 1;
a << 64;
正如 YSC 所解释的,在一次操作中移动 超过类型大小的 是未定义的行为;这条规则来自于将位移运算符直接映射到机器代码指令的愿望,在这种情况下,机器代码指令具有不同的行为,具体取决于处理器。
例如,在 x86 上,SHL 指令用 63 屏蔽移位量(在 64 位寄存器上操作时),这可能是您看到 a<<64
保持为 1 的原因(因为 64 & 63 == 0
,因此它实际上是一个空操作)。
请注意,这只是一个指导性解释,通常适用于简单情况(通常,禁用优化,或启用优化但移位量未知,因此当移位确实映射到底层平台移位操作码时)。当用常量移动常量时,编译器可能会传播值并在内部以更高的精度执行算术运算,或者,即使在一般情况下,发出在大于您的数据类型的寄存器中工作的代码并在末尾截断(例如,合法的是将 uint32_t
的移位映射到完整的 64 位寄存器移位,尽管不是特别聪明),因此在这些超出规格的情况下会给出不同的结果。请记住:未定义的行为是未定义的,您不能真正期望发生任何特定的事情。
另一方面,分两步进行操作按预期工作,因为这两个操作都是明确定义的(它们在右侧填充零,丢弃左侧的位)。