左移产生奇怪结果(如果s == 64,则为1ull << s == 1)

时间:2019-03-27 11:37:18

标签: c++

为什么是

的结果
uint32_t s = 64;
uint64_t val = 1ull << s;

uint64_t s = 64;
uint64_t val = 1ull << s;

1? 但是

uint64_t val = 1ull << 0x40;

优化到0? 我真的不明白为什么它等于1。无论我使用VC ++还是g ++编译器,都是如此。

1ull << s等于64时,如何确保s等于0,我认为正确的结果是什么?我还需要imo。我程序中的结果正确。

1 个答案:

答案 0 :(得分:2)

这是因为在x64上,指令SHL(在64位源/目标操作数上操作时)仅使用移位量的低6位。实际上,您正在移动0位。

从“ SAL / SAR / SHL / SHR-Shift”条目下的“ Intel 64和IA-32体系结构软件开发人员手册”(可以从Intel下载PDF格式,很难链接到)。说明:

  

计数被屏蔽为5位(如果在64位模式下使用REX.W,则为6位)。计数范围限制为0到31(如果使用64位模式和REX.W,则为63)。

如下所述,在C ++语言中,将整数移位比其大小还多的位也是“未定义行为”。 (感谢@sgarizvi提供参考。)C ++标准在8.5.7节(移位运算符)下指出:

  

如果右操作数为负,或者大于或等于提升后的左操作数的位长度,则行为不确定。

这就是为什么编译器生成的代码在不同条件下(恒定或可变移位计数,是否优化等)产生不同结果的原因

关于如何“修复”它,我没有聪明的窍门。您可以执行以下操作:

template <typename IntT>
IntT ShiftLeft (IntT x, unsigned count) {
    // Optional check; depends on how much you want to embrace C++!
    static_assert(std::is_integral_v<IntT>, "This shift only works for integral types.");

    if (count < sizeof(IntT) * 8)
        return x << count;
    else
        return 0;
}

该代码适用于有符号和无符号整数类型(有符号和无符号值的左移相同)。