如何检查 int 中是否恰好设置了一位?

How to check if exactly one bit is set in an int?

本文关键字:设置 一位 是否 何检查 检查 int      更新时间:2023-10-16

我有一个std::uint32_t,想检查是否设置了一位。如何在不像这样遍历所有位的情况下执行此操作?换句话说,下面的功能可以简化吗?

static inline bool isExactlyOneBitSet(std::uint32_t bits)
{
return ((bits & 1) == bits
|| (bits & 1 << 1) == bits
|| (bits & 1 << 2) == bits
// ...
|| (bits & 1 << 31) == bits
);
}

奖励:如果返回值是找到的位或 0,那就太好了。

static inline bool isExactlyOneBitSet(std::uint32_t bits)
{
if (bits & 1) {return 1;}
else if  (bits & 1 << 1) {return 1 << 1;};
//...
else if  (bits & 1 << 31) {return 1 << 31;};
return 0;
}

所以你想知道一个数字是否是 2 的幂?嗯,有一个著名的算法,你可以简单地做,

check_bit(std::uint32_t bits)
{
return bits && !(bits & (bits-1));
}

2 的任何幂减去 1 都是1s的,例如,

4 - 1 = 3 (011)
8 - 1 = 7 (0111)

按位和 2 的任何幂和任何数字 1 的幂小于它将给出0.因此,我们可以通过使用表达式来验证一个数字是否为 2 的幂,n&(n-1).

n=0时会失败,因此我们必须添加一个额外的and条件。

要查找位的位置,您可以执行以下操作:

int findSetBit(std::uint32_t bits)
{
if (!(bits && !(bits & (bits-1))))
return 0;
return log2(bits) + 1;
}

额外的东西

在 gcc 中,您可以使用__builtin_popcount()来查找任意数字的集合位计数。

#include <iostream>
int main()
{
std::cout << __builtin_popcount (4) << "n";
std::cout << __builtin_popcount (3) << "n";
return 0;
}

然后检查计数是否等于1

关于计数,还有另一种著名的算法,Brian Kernighan算法。谷歌一下,它会在log(n)时间内找到计数。

这是您的奖金问题的解决方案(当然,这也是您原始问题的解决方案(:

std::uint32_t exactlyOneBitSet(std::uint32_t bits) {
return bits&(((bool)(bits&(bits-1)))-1);
}

这在带有 clang 的 x86_64 上编译为只有 4 条指令:

0000000000000000 <exactlyOneBitSet(unsigned int)>:
0:   8d 4f ff                lea    -0x1(%rdi),%ecx
3:   31 c0                   xor    %eax,%eax
5:   85 f9                   test   %edi,%ecx
7:   0f 44 c7                cmove  %edi,%eax
a:   c3                      retq   

C++20 有一个函数可以做到这一点:std::has_single_bit(value(。 它在标题<bit>中。

给定一个数字n,下面的表达式将确定是否只设置了一个位(在任何语言中(:(n & (n - 1)) == 0

要检查一个数字是否是 2 的精确幂,您还可以使用表达式(n & (-n)) == n

但是,在JavaScript 中,这些表达式在-2^31时失败。

请注意此处的按位 AND (&( 运算符。