【问题标题】:How to check if exactly one bit is set in an int?如何检查 int 中是否设置了一个位?
【发布时间】:2018-12-08 05:27:17
【问题描述】:

我有一个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^n 的数字只是设置了一位的数字。
  • 正好一个还是至少一个?您的第一个示例至少检查一个,相当于!= 0。
  • x &amp;&amp; !(x &amp; (x-1))
  • FWIW,C++20 可能会有std::ispow2。
  • 奖励答案:return bits;.

标签: c++ bit-manipulation


【解决方案1】:

所以你想知道一个数字是否是 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。因此,我们可以使用表达式n&amp;(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;
}

然后检查count是否等于1。

关于计数,还有另一个著名的算法,Brian Kernighan 算法。谷歌一下,它发现计数在log(n)时间。

【讨论】:

  • stackoverflow.com/questions/11114017/… 了解 __buildtin_popcount 的跨平台交叉编译器变体的概述
  • 感谢您提及该链接,popcount 函数取决于编译器。
  • 在奖励中,不是期望值位,而不是 log2 位吗?
  • op想要找到'on'位的位置。
【解决方案2】:

这是您的附加问题的解决方案(当然,它也是您原始问题的解决方案):

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

这在 x86_64 上用 clang 编译成只有 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   

【讨论】:

    猜你喜欢
    • 2021-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-23
    • 2011-03-09
    • 1970-01-01
    相关资源
    最近更新 更多