警告C4146减去无符号类型的运算符

use*_*254 3 c++ unsigned compiler-errors

我从我想要使用的库中获取此代码.在编译时,我收到以下警告:

警告C4146:一元减号运算符应用于无符号类型,结果仍未签名

inline int lastbit (uint32_t v)
{
  int r;
  static const int MultiplyDeBruijnBitPosition[32] = 
    {
      0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 
      31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
    };
  r = MultiplyDeBruijnBitPosition[((uint32_t)((v & -v) * 0x077CB531U)) >> 27];
  return r;
}
Run Code Online (Sandbox Code Playgroud)

如何通过尽可能少地更改库来修复它?

Adr*_*thy 7

vis 的类型,是std::uint32_t无符号类型.无符号类型通常用于索引和计数,因为它们永远不会是负数.

试图在无符号数字上翻转符号通常是可疑的,这就是编译器发出警告的原因.然而,在这种情况下,它是安全且定义明确的,并且库依赖于在无符号数字上翻转符号的确切含义的细节.

从C++ 11标准:

通过从2 ^ n中减去其值来计算无符号数量的负数,其中n是提升的操作数中的位数.结果的类型是提升的操作数的类型.[第5.3.1.8节]

[标准表示2 ^ n,这意味着字面意思,即使2 ^ n不能用无符号类型的n位表示.在不使用更大类型的情况下实现此方法的最常见方法是翻转所有位,然后添加一个: neg_v = ~v + 1;.

要说服编译器此操作正常,您可以尝试使用强制转换.(当您需要强制编译器将值视为其自然类型的其他值时,应该很少使用强制转换.)

const uint32_t neg_v = static_cast<uint32_t>(-static_cast<int32_t>(v));
r = MultiplyDeBruijnBitPosition[((uint32_t)((v & neg_v) * 0x077CB531U)) >> 27];
Run Code Online (Sandbox Code Playgroud)

内部强制转换要求编译器转换v为32位有符号整数.对于v最多2 ^ 31 - 1的值,这会产生相同的值.对于较大的值v,这将导致负值.

但是现在你正在翻转一个有符号值的符号(编译器很乐意这样做),但标准不再能保证它完全相同.(所有现代机器都使用二进制补码,因此实际上它会产生相同的结果.)

如果你想要挑剔(像我一样),你可以使用上面的二进制补码技巧直接对无符号值执行按位运算.而不是-v,你有(~v + 1u):

r = MultiplyDeBruijnBitPosition[((uint32_t)((v & (~v + 1u)) * 0x077CB531U)) >> 27];
Run Code Online (Sandbox Code Playgroud)

  • “但是所有现代机器都使用二进制补码,因此实际上它会产生相同的结果。” -即使在补码机器上,也不保证。我喜欢您的第二个选项,但是第三个选项是通过#pragma`或通过项目选项简单地抑制警告,这就是我想要的选择。 (2认同)