我从我想要使用的库中获取此代码。在编译时,我收到以下警告:
警告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;
}
如何通过尽可能少地更改库来修复它?
答案 0 :(得分:7)
v
的类型为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];
内部强制转换要求编译器将v
转换为32位有符号整数。对于v
最多2 ^ 31 - 1的值,这会得到相同的值。对于较大的v
值,这将导致负值。
但是现在你正在翻转一个有符号值的符号(编译器很乐意这样做),但标准不再能保证完全做同样的事情。 (所有现代机器都使用两个补码,因此实际上它会产生相同的结果。)
如果你想挑剔(像我一样),你可以使用上面的两个补码技巧直接对无符号值执行按位运算。您没有-v
,而是(~v + 1u)
:
r = MultiplyDeBruijnBitPosition[((uint32_t)((v & (~v + 1u)) * 0x077CB531U)) >> 27];