3

我发现将 uint64_t 值向右移动 64 位或更多位时会出现异常结果。我本来希望下面的代码输出值 0,但它没有 - 它输出 value>>2 - 但仅限于 g++。

#include <iostream>
#include <cstdint>
using namespace std;
int main(void)
{
    uint64_t value = 0x5d4d629e80d5489UL;
    int shift = 66;
    cout << hex << (value >> shift) << endl;
    return 0;
}

我编译并运行:

$ g++ -std=c++0x mad.cpp
$ ./a.out
175358a7a035522
$ uname -a
Linux svr 3.2.0-26-generic #41-Ubuntu SMP Thu Jun 14 17:49:24 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux
$ g++ --version 
g++ (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3

我在另一个带有 g++ (Debian 4.4.5-8) 4.4.5 的 Linux 机器上得到了相同的行为。但是,当我使用 Visual Studio C++(Express Edition)编译它时,我没有得到相同的行为 - 我得到了我最初预期的结果 - 即 0。

我想知道当 x 和 y 是序数并且 y 大于 x 中的位数时,假设 C++ 中的 x>>y 应该产生 0 是否应该是安全的。这是一个 g++ 错误 - 还是这里不符合标准的 Visual C++?我假设这不是/不应该是未定义的行为?这可能是特定于 CPU 的吗?

我意识到我总是可以明确地检查超过 8*sizeof(value) 的每个班次——但这会引入一个额外的分支,我希望在某些性能关键代码中避免。

这是一个已知问题吗?如果是的话,为了实现一致的跨平台评估,推荐的策略是什么?

4

3 回答 3

9

来自关于移位运算符的 C++ 标准:

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

于 2012-07-16T00:54:20.123 回答
1

此行为未定义。
考虑阅读一些关于移位太多是如何格式错误以及如何保护自己免受它的影响的阅读:INT34-C。不要移动负数或比操作数中存在的更多位

于 2012-07-16T00:57:12.337 回答
0

未定义行为的一个原因是它取决于底层硬件,它以不同的方式实现转变。

在最初的 8086 上,移位是按照要求的次数执行的,每个位置使用一个时钟滴答。很快就发现这导致保证的中断响应时间很糟糕

所以从 286 开始,硬件会屏蔽移位计数并且只使用低位。这就是当 66 变为 2 位置移位时所看到的。

于 2012-07-16T08:00:30.487 回答