如何计算整数中的零位数?

时间:2010-11-22 10:09:05

标签: c++ bits

我如何在C ++中找到'零'位的数量。 假设我有一个整数;

int value = 276; 

我有100010100位,但我如何计算零?

12 个答案:

答案 0 :(得分:26)

如果你想要效率,那么“Hackers Delight”一书中有一个很好的实现

22条指令免费分支。

unsigned int count_1bits(unsigned int x)
{
    x = x - ((x >> 1) & 0x55555555);
    x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
    x = x + (x >> 8);
    x = x + (x >> 16);
    return x & 0x0000003F;
}

unsigned int count_0bits(unsigned int x)
{
    return 32 - count_1bits(x);
}

我会尝试解释它是如何工作的。这是一种分而治之的算法。

(x >> 1) & 0x55555555

将所有位向右移1步,并取每个位对的最低位。

0x55555555 -> 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 (16x2 bit pairs)

所以基本上你将得到所有2位排列的下表。

1. (00 >> 1) & 01 = 00
2. (01 >> 1) & 01 = 00
3. (10 >> 1) & 01 = 01
4. (11 >> 1) & 01 = 01

x - ((x >> 1) & 0x55555555);

然后从非移位对中减去这些。

1. 00 - 00 = 00 => 0 x 1 bits
2. 01 - 00 = 01 => 1 x 1 bits
3. 10 - 01 = 01 => 1 x 1 bits
4. 11 - 01 = 10 => 2 x 1 bits

x = x - ((x >> 1) & 0x55555555);

所以现在我们已经更改了每2位对,因此它们的值现在是它们对应的原始2位对的位数...然后我们继续以类似的方式使用4位组,8位组,16位小组和最后的32位。

如果您想要更好的解释购买本书,有很多很好的解释和讨论替代算法等...

答案 1 :(得分:15)

最简单最天真的方法是迭代位数并计算:

size_t num_zeroes = 0;

for(size_t i = 0; i < CHAR_BIT * sizeof value; ++i)
{
  if ((value & (1 << i)) == 0)
    ++num_zeroes;
}

有许多更好的(对于不同的“更好”的值)方式,但这很清楚,非常简洁(代码方面),并且不需要一堆设置。

可能被视为改进的一个微优化是不计算掩码来测试每个位,而是移动值并始终测试最右边的位:

for(size_t i = 0; i < CHAR_BIT * sizeof value; ++i, value >>= 1)
{
  if ((value & 1) == 0)
    ++num_zeroes;
}

答案 2 :(得分:9)

你可以减去32 the number of bits set

答案 3 :(得分:4)

如果您使用GCC,您可以尝试内置功能:

int __builtin_popcount (unsigned int x) 
int __builtin_ctz (unsigned int x)
int __builtin_clz (unsigned int x)

有关详细信息,请参阅GCC Documentation

答案 4 :(得分:4)

Kernighan way计数设置位

unsigned int v; // count the number of bits set in v
unsigned int c; // c accumulates the total bits set in v
for (c = 0; v; c++)
{
  v &= v - 1; // clear the least significant bit set
}

可以轻松适应给定的任务。这里的多次迭代等于设置的位数。

我还推荐上面的链接,用于解决此问题以及其他类型的位相关任务的各种其他方法。还有一个单行示例获取在宏中实现的位计数。

答案 5 :(得分:3)

到目前为止,最明显的解决方案是查找表。

/* Assuming CHAR_BITS == 8 */
int bitsPerByte[256] = { 8, 7, 7, 6, /* ... */ };
int bitsInByte(unsigned char c) { return bits[c]; }

答案 6 :(得分:3)

这本书有一本很好的书:Hacker's Delight(是的,这个名字很糟糕:它与安全无关,但却完全无聊)。它提供了几种算法来计算'1'位,也可以找到最好的here(虽然这本书解释说这个网站没有)。

一旦知道'1'位计数,只需将其减去类型表示中的位数。

答案 7 :(得分:2)

我很惊讶没有人提到过这个:

int num_zero_bits = __builtin_popcount(~num);

当与GCC一起使用时,这将给出num中的零位数。

答案 8 :(得分:1)

做一个人的恭维然后算上1秒。

count_zero_bits(x)= count_one_bits(~x);

实施代码来计算代码。

template< typename I > 
int count_one_bits( I i )
{
   size_t numbits = 0;
   for( ; i != 0; i >>= 1 )
   {
      numbits += i&1;
   }
}

虽然我的功能存在问题,如果我是负数,因为&gt;&gt;将1位放入右侧,这样您就可以得到一个永不停止的循环。如果有一种模板化的方法来强制执行理想的无符号类型。

一旦你拥有了那个:

template< typename I > int count_zero_bits( I i )
{
   return count_one_bits( ~i );
}

会奏效。

答案 9 :(得分:0)

据我所知,将零位计数置于正整数中的最简单方法是以下代码。

int get_zero_bit_count(int num)
{
    int cnt = 0;

    while(num > 0)
        {
            int and_num = num & 1;

            if (and_num != num) cnt++;

            num >>= 1; 
        }

        return cnt;
    }

这段代码很容易理解,并且是自我解释的。这适用于正整数。

答案 10 :(得分:0)

扩展ronag的答案,其他用户提到的导致错误的结果(他的算法最多只能达到x = 15的值),这里是算法的更新版本:

uint8_t count_1bits(uint32_t x) {
    x = x - ((x >> 1) & 0x55555555);
    x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
    x = (x & 0x0F0F0F0F) + ((x >> 4) & 0x0F0F0F0F);
    x = (x & 0x00FF00FF) + ((x >> 8) & 0x00FF00FF);
    x = (x & 0x0000FFFF) + ((x >> 16) & 0x0000FFFF);
    return x & 0x3F;
}

uint8_t count_0bits(uint32_t x)    {
    return 32 - count_1bits(x);
}

对ronag的第一行的解释是正确的,但是,其余的行使用不同的方法。在第一行中,通过移位和减法,每个2位对将包含原始数中该对中设置的位数。通过将每个2n位组的lsb添加到移位n的该对的msb,其余的行递归地将这些数字折叠在一起,以便2n位组包含在该组中设置的位数。原始号码:

01110110: 0111 (7 bits were set in the original number) 0110 (6 bits were set in the original number)
-> 01110110 & 00001111 + (01110110 >> 4) & 00001111
= 0110 + 0111
= 1101

上述算法适用于32位整数,但可以通过将常数更改为正确的位长度来轻松调整,以使模式保持不变(例如0x5555 ... = 0101 ...,0x0f0f ... = 00001111 ......等)并添加/删除适当的班次

答案 11 :(得分:0)

#include <bits/stdc++.h>
using namespace std;
#define ll long long int


int main() {
    ll a;
    cin>>a;
    ll ones=__builtin_popcountll(~a);
    ll zero=__builtin_clzll(a);
    ll num=abs(ones-zero);
    cout<<num<<"\n";
    return 0;
}

借助内置的gcc函数,即popcount(设置位的数量)和clz(前导零的数量),我们可以计算整数中的零位数量

您可以在https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.htmlhttps://www.geeksforgeeks.org/builtin-functions-gcc-compiler/

上了解它们。