我从我想使用的库中获得了这段代码。编译时,我收到以下警告:
警告 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;
}
如何通过尽可能少地更改库来修复它?
最佳答案
2021-05-18 更新:来自 Raymond Chen's blog 的更好解决方案:
neg_v = (0 - v)
编译器不会发出警告,因为无符号减法是明确定义的,即使右侧的值大于左侧的值也是如此。您可以编写 0u
而不是 0
,但我认为积分促销会为您解决这个问题。
下面是原始答案
v
的类型是 std::uint32_t
,这是一个无符号类型。无符号类型通常用于索引和计数,因为它们永远不会为负数。
尝试翻转无符号数的符号通常是可疑的,这就是编译器发出警告的原因。然而,在这种情况下,它是安全且定义明确的,并且库依赖于翻转无符号数字上的符号究竟意味着什么的详细信息。
来自 C++11 标准:
The negative of an unsigned quantity is computed by subtracting its value from the 2^n, where n is the number of bits in the promoted operand. The type of the result is the type of the promoted operand. [Section 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];
内部转换要求编译器将 v
转换为 32 位有符号整数。对于最大 2^31 - 1 的 v
值,这会产生相同的值。对于较大的 v
值,这将导致负值。
但现在您要翻转有符号值的符号(编译器会很乐意这样做),但标准不再保证做完全相同的事情。 (所有现代机器都使用二进制补码,因此实际上会给出相同的结果。)
如果你想要挑剔(像我一样),你可以直接对无符号值执行按位运算,使用上面的补码技巧。而不是 -v
,你有 (~v + 1u)
:
r = MultiplyDeBruijnBitPosition[((uint32_t)((v & (~v + 1u)) * 0x077CB531U)) >> 27];
关于c++ - 警告 C4146 减去无符号类型的运算符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26892674/