
[英]error C4146: unary minus operator applied to unsigned type, result still unsigned
[英]warning C4146 minus operator on unsigned type
我有一个来自我想使用的库的代码。 在编译时,我收到以下警告:
警告 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 博客的更好的解决方案:
neg_v = (0 - v)
编译器不会发出警告,因为即使右侧的值大于左侧的值,无符号减法也是明确定义的。 你可以写0u
而不是0
,但我认为积分促销会为你解决这个问题。
下面的原答案
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 位有符号整数。 对于高达 2^31 - 1 的v
值,这会产生相同的值。 对于较大的v
值,这将导致负值。
但是现在您要在有符号值上翻转符号(编译器会很乐意这样做),但标准不再保证做完全相同的事情。 (所有现代机器都使用二进制补码,因此实际上,它会给出相同的结果。)
如果你想挑剔(像我一样),你可以直接对无符号值执行按位运算,使用上面的二进制补码技巧。 而不是-v
,你有(~v + 1u)
:
r = MultiplyDeBruijnBitPosition[((uint32_t)((v & (~v + 1u)) * 0x077CB531U)) >> 27];
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.