左移位和丢弃位

Ser*_*eyA 6 c++ integer-overflow bit-shift language-lawyer

让我们考虑一个函数(它的可能实现之一),它将无符号短值(或任何其他无符号整数类型)的右N位置零.可能的实现可能如下所示:

template<unsigned int shift>
unsigned short zero_right(unsigned short arg) {
  using type = unsigned short;

  constexpr type mask = ~(type(0));
  constexpr type right_zeros = mask << shift; // <-- error here
  return arg & right_zeros;
}

int check() {
  return zero_right<4>(16);
}
Run Code Online (Sandbox Code Playgroud)

使用此代码,我可以访问的所有编译器以某种方式抱怨可能的溢出.CLang是最明确的一个,有以下明确的信息:

错误:从'int'到'const type'的隐式转换(又名'const unsigned short')将值从1048560更改为65520 [-Werror,-Wconstant-conversion]

这个代码看起来很清晰,对我来说很明显,但是当3个编译器抱怨时,我变得非常紧张.我在这里错过了什么吗?真的有可能发生腥病吗?

PS虽然zeriong out左边X位的替代实现可能是受欢迎和有趣的,但这个问题的主要焦点是发布的代码的有效性.

M.M*_*M.M 2

该消息看起来很简单:

错误:从“int”到“const type”(又名“const unsigned Short”)的隐式转换将值从 1048560 更改为 65520 [-Werror,-Wconstant-conversion]

mask << shift具有值1048560(源自65535 << 4),并将其分配给unsigned short,它被定义为调整值mod 65536,给出65520

最后一个转换是明确定义的。错误消息是因为您传递了编译器标志,-Werror,-Wconstant-conversion请求无论如何在这种情况下获取错误消息。如果您不希望出现此错误,则不要传递这些标志。


尽管这个特定的用法是明确定义的,但某些输入可能存在未定义的行为(即,如果您在 32 位 int 系统上,则为shiftbeing或更大)。16所以你应该修复这个功能。

要修复该函数,您需要在这种unsigned short情况下更加小心,因为有关从 unsigned Short 到signed int 的整数提升的极其烦人的规则。

这是一种与其他产品有点不同的解决方案..完全避免班次问题,适用于任何班次大小:

template<unsigned int shift, typename T>
constexpr T zero_right(T arg)
{
    T mask = -1;
    for (int s = shift; s--; ) mask *= 2u;
    return mask & arg;
}

// Demo
auto f() { return zero_right<15>((unsigned short)65535); }  //  mov eax, 32768
Run Code Online (Sandbox Code Playgroud)