我有一个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;
}
答案 0 :(得分:4)
所以您想知道一个数字是否为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的任何数字1将得到0
。因此,我们可以使用表达式n&(n-1)
来验证数字是否为2的幂。
在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的算法。用Google搜索它,它会在log(n)
时间内找到计数。
答案 1 :(得分:1)
这是您的红利问题的解决方案(当然,这也是您原来的问题的解决方案):
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