这段代码如何计算1位数?

时间:2021-09-11 03:00:01

I found the following code to count the number of 1-bits in binary representation for given integer. Can anyone explain how it works? And how are the bit masks chosen for such task? Thanks.

我找到以下代码来计算给定整数的二进制表示中的1位数。谁能解释它是如何工作的?如何为这样的任务选择位掩码?谢谢。

int count_one(int x)
{
    x = (x & (0x55555555)) + ((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;
}

4 个解决方案

#1


8  

This algorithm uses x as both the source of the computation and as memory. First, think about what the bitmasks are:

该算法使用x作为计算源和存储器。首先,考虑一下bitmasks是什么:

0x55 = 01010101
0x33 = 00110011
0x0f = 00001111
0xff = 11111111

Let's go with an 8 bit example: a = 01101010

让我们看一个8位的例子:a = 01101010

a & 0x55 = 01000000; (a >> 1) & 0x55 = 00010101 

If we add these two together, we get the bit count of each two bit pair. This result is at most 0x10, as the mask has only one bit set for each addition.

如果我们将这两者加在一起,我们得到每两位对的位数。此结果最多为0x10,因为掩码每次添加仅设置一个位。

Now, we use the 0x33 mask, remember each consecutive 2 bits contain the result from the first operation. With this mask, we mask out consecutive 4 bits and add them. This result is at most 0x100. This goes on with the other masks, until we have the result in x.

现在,我们使用0x33掩码,记住每个连续的2位包含第一次操作的结果。使用此掩码,我们屏蔽连续的4位并添加它们。此结果最多为0x100。这继续使用其他掩码,直到我们得到x的结果。

Try it on paper, it will be pretty clear after a few steps.

在纸上试一下,经过几个步骤后会很清楚。

#2


4  

It's a divide and conquer approach. Note that the first line will give you the sum on subsequent pairs, next the sum on subsequent quadruples,... and finally the number of bits.

这是一种分而治之的方法。请注意,第一行将为后续对提供总和,接下来是后续四元组的总和,...以及最后的位数。

Example (16 bit so consider your code without the last line)

示例(16位因此请考虑没有最后一行的代码)

1011001111000110

In the fist line we take & with even bits and odd bits shifted by one. Then we add.

在第一行中,我们采用&偶数位和奇数位移一位。然后我们补充说。

For even bits:

对于偶数位:

num:  10 11 00 11 11 00 01 10
mask: 01 01 01 01 01 01 01 01
res:  00 01 00 01 01 00 01 00

For odd bits:

对于奇数位:

num>>1: 01 01 10 01 11 10 00 11
mask:   01 01 01 01 01 01 01 01
res:    01 01 00 01 01 00 00 01

We add those results and get sums in subsequent pairs

我们添加这些结果并在后续对中获得总和

01 10 00 10 10 00 01 01

We repeat the same with following masks and corresponding shifts

我们用以下面具和相应的班次重复相同的操作

2nd: 0011 0011 0011 0011
3rd: 0000 1111 0000 1111
4th: 0000 0000 1111 1111

And we get:

我们得到:

2nd: 0011 0010 0010 0010  // 3 set in the first four, 2 in other quadrupels
3rd: 0000 0101 0000 0100  // 5 bits set in the first eight, 4 in the rest
4th: 0000 0000 0000 1001  // 9 bits in total

#3


2  

To make this easier to explain, let me suppose an integer is 4 bits long and each bit is represent as 1,2,3,4. In order to get the number of 1-bits, you can first get the sum of 1 and 2 and the sum of 3 and 4 and then add these sums up.

为了使这更容易解释,让我假设一个整数是4位长,每个位表示为1,2,3,4。为了获得1位的数量,您可以首先得到1和2的总和以及3和4的总和,然后将这些总和相加。

x & (0x5) will eliminate 1 and 3, and x & (0xa) will eliminate 2 and 4. x & (0x5) + (x & (0xa) >> 1) will use 1 2 bits to store the sum of 1 and 2 and use 3 4 bits to store the sum of 3 and 4. x & (0x5) + (x & (0xa) >> 1) is same as x & (0x5) + (x >> 1) & (0x5).

x&(0x5)将消除1和3,x&(0xa)将消除2和4. x&(0x5)+(x&(0xa)>> 1)将使用1 2位来存储1的总和和2并使用3 4位来存储3和4之和.x&(0x5)+(x&(0xa)>> 1)与x&(0x5)+(x >> 1)&(0x5)相同)。

Now we have the sums of 1 2 and 3 4 all stored in x, we can get the result after we add them up. Same as above, x & (0x3) will get the sum of 3 4 and x & (0x12) will get the sum of 1 2. x & (0x3) + (x & (0x12)) >> 2 will get the final result. x & (0x3) + (x & (0x12)) >> 2 is same as x & (0x3) + (x >> 2) & (0x3).

现在我们将1 2和3 4的总和全部存储在x中,我们可以在添加它们之后得到结果。与上面相同,x&(0x3)将获得3 4的总和,x&(0x12)将得到1的总和.x&(0x3)+(x&(0x12))>> 2将获得最终结果。 x&(0x3)+(x&(0x12))>> 2与x&(0x3)+(x >> 2)&(0x3)相同。

So you can see what happen inside. In your case, the first line you can get the sum of 1 2 and 3 4 and 5 6 and go on. And in the second line you get the sum of 1 2 3 4 and 5 6 7 8 and go on. So by doing this 5 times, you get the number of all 32 bits.

所以你可以看到里面发生了什么。在你的情况下,第一行你可以得到1 2和3 4和5 6的总和,然后继续。在第二行,你得到1 2 3 4和5 6 7 8的总和,然后继续。所以通过这样做5次,你得到所有32位的数量。

#4


1  

The first line treats the integer as an array of 16 2-bit integers, the result of the expression is 0 if 0 bits in the 2-bit pair were set, 1 if 1 bit in the 2-bit pair was set (01 bin or 10 bin), and 2 if both bits of the 2 bit pair were set. This is because we consider the lower of every two bits of x, and the lower of every two bits of x shifted right by one; adding them together. We know no carries will occur outside of the 2-bit pairs because our summands are limited to 0 or 1. The result is then stored back into x.

第一行将整数视为16个2位整数的数组,如果设置了2位对中的0位,则表达式的结果为0;如果设置了2位对中的1位,则表达式为1(01 bin或者10 bin),如果设置了2位对的两个位,则为2。这是因为我们考虑x的每两位中的较低位,并且x的每两位中的较低位右移一位;将它们加在一起。我们知道在2位对之外不会发生进位,因为我们的求和被限制为0或1.然后将结果存储回x。

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

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

The next line does the same thing, this time treating every 2-bits as a separate integer, storing their sum in every 4-bits the two summands used to occupy. After this operation the integer essentially becomes an array of 8 4-bit integers. Before the operation each summand is either 0, 1, or 2 (decimal) because it corresponds to the counts from the last line. Because of this we know each sum will be no greater than 4. Since each 4-bit int has a maximum value of 15 (decimal), we know this will also not overflow. As above, the result is stored back into x.

下一行做同样的事情,这次将每2位作为一个单独的整数处理,将它们的总和存储在用于占用的两个加数的每4位中。在此操作之后,整数基本上变为8个4位整数的数组。在操作之前,每个summand是0,1或2(十进制),因为它对应于最后一行的计数。因此我们知道每个总和不会大于4.因为每个4位int的最大值为15(十进制),我们知道这也不会溢出。如上所述,结果存储回x。

x = (x & (0x33333333)) + ((x >> 2) & (0x33333333));

x =(x&(0x33333333))+((x >> 2)&(0x33333333));

We do the same as above, this time suming every pair of 4-bits into every set of 8 bits.

我们也是如上所述,这次将每对4位加到每组8位中。

x = (x & (0x0f0f0f0f)) + ((x >> 4) & (0x0f0f0f0f));

x =(x&(0x0f0f0f0f))+((x >> 4)&(0x0f0f0f0f));

We then sum ever pair of 8-bits into every pair of 16 bits (the upper and lower half of x).

然后,我们将每对16位(x的上半部分和下半部分)中的一对8位相加。

x = (x & (0x00ff00ff)) + ((x >> 8) & (0x00ff00ff));

x =(x&(0x00ff00ff))+((x >> 8)&(0x00ff00ff));

We now sum the upper and lower half of x, and we are left with a 32-bit value equal to the number of bits set in the value of x.

我们现在将x的上半部分和下半部分相加,并且我们留下32位值,该值等于x值中设置的位数。

x = (x & (0x0000ffff)) + ((x >> 16) & (0x0000ffff));

x =(x&(0x0000ffff))+((x >> 16)&(0x0000ffff));

The key here is that each step does an inplace pairwise count of bits until we are left with the total bit count in the 32-bit integer it self.

这里的关键是每一步都进行一个就位的成对位计数,直到我们留下32位整数的总位数。

#1


8  

This algorithm uses x as both the source of the computation and as memory. First, think about what the bitmasks are:

该算法使用x作为计算源和存储器。首先,考虑一下bitmasks是什么:

0x55 = 01010101
0x33 = 00110011
0x0f = 00001111
0xff = 11111111

Let's go with an 8 bit example: a = 01101010

让我们看一个8位的例子:a = 01101010

a & 0x55 = 01000000; (a >> 1) & 0x55 = 00010101 

If we add these two together, we get the bit count of each two bit pair. This result is at most 0x10, as the mask has only one bit set for each addition.

如果我们将这两者加在一起,我们得到每两位对的位数。此结果最多为0x10,因为掩码每次添加仅设置一个位。

Now, we use the 0x33 mask, remember each consecutive 2 bits contain the result from the first operation. With this mask, we mask out consecutive 4 bits and add them. This result is at most 0x100. This goes on with the other masks, until we have the result in x.

现在,我们使用0x33掩码,记住每个连续的2位包含第一次操作的结果。使用此掩码,我们屏蔽连续的4位并添加它们。此结果最多为0x100。这继续使用其他掩码,直到我们得到x的结果。

Try it on paper, it will be pretty clear after a few steps.

在纸上试一下,经过几个步骤后会很清楚。

#2


4  

It's a divide and conquer approach. Note that the first line will give you the sum on subsequent pairs, next the sum on subsequent quadruples,... and finally the number of bits.

这是一种分而治之的方法。请注意,第一行将为后续对提供总和,接下来是后续四元组的总和,...以及最后的位数。

Example (16 bit so consider your code without the last line)

示例(16位因此请考虑没有最后一行的代码)

1011001111000110

In the fist line we take & with even bits and odd bits shifted by one. Then we add.

在第一行中,我们采用&偶数位和奇数位移一位。然后我们补充说。

For even bits:

对于偶数位:

num:  10 11 00 11 11 00 01 10
mask: 01 01 01 01 01 01 01 01
res:  00 01 00 01 01 00 01 00

For odd bits:

对于奇数位:

num>>1: 01 01 10 01 11 10 00 11
mask:   01 01 01 01 01 01 01 01
res:    01 01 00 01 01 00 00 01

We add those results and get sums in subsequent pairs

我们添加这些结果并在后续对中获得总和

01 10 00 10 10 00 01 01

We repeat the same with following masks and corresponding shifts

我们用以下面具和相应的班次重复相同的操作

2nd: 0011 0011 0011 0011
3rd: 0000 1111 0000 1111
4th: 0000 0000 1111 1111

And we get:

我们得到:

2nd: 0011 0010 0010 0010  // 3 set in the first four, 2 in other quadrupels
3rd: 0000 0101 0000 0100  // 5 bits set in the first eight, 4 in the rest
4th: 0000 0000 0000 1001  // 9 bits in total

#3


2  

To make this easier to explain, let me suppose an integer is 4 bits long and each bit is represent as 1,2,3,4. In order to get the number of 1-bits, you can first get the sum of 1 and 2 and the sum of 3 and 4 and then add these sums up.

为了使这更容易解释,让我假设一个整数是4位长,每个位表示为1,2,3,4。为了获得1位的数量,您可以首先得到1和2的总和以及3和4的总和,然后将这些总和相加。

x & (0x5) will eliminate 1 and 3, and x & (0xa) will eliminate 2 and 4. x & (0x5) + (x & (0xa) >> 1) will use 1 2 bits to store the sum of 1 and 2 and use 3 4 bits to store the sum of 3 and 4. x & (0x5) + (x & (0xa) >> 1) is same as x & (0x5) + (x >> 1) & (0x5).

x&(0x5)将消除1和3,x&(0xa)将消除2和4. x&(0x5)+(x&(0xa)>> 1)将使用1 2位来存储1的总和和2并使用3 4位来存储3和4之和.x&(0x5)+(x&(0xa)>> 1)与x&(0x5)+(x >> 1)&(0x5)相同)。

Now we have the sums of 1 2 and 3 4 all stored in x, we can get the result after we add them up. Same as above, x & (0x3) will get the sum of 3 4 and x & (0x12) will get the sum of 1 2. x & (0x3) + (x & (0x12)) >> 2 will get the final result. x & (0x3) + (x & (0x12)) >> 2 is same as x & (0x3) + (x >> 2) & (0x3).

现在我们将1 2和3 4的总和全部存储在x中,我们可以在添加它们之后得到结果。与上面相同,x&(0x3)将获得3 4的总和,x&(0x12)将得到1的总和.x&(0x3)+(x&(0x12))>> 2将获得最终结果。 x&(0x3)+(x&(0x12))>> 2与x&(0x3)+(x >> 2)&(0x3)相同。

So you can see what happen inside. In your case, the first line you can get the sum of 1 2 and 3 4 and 5 6 and go on. And in the second line you get the sum of 1 2 3 4 and 5 6 7 8 and go on. So by doing this 5 times, you get the number of all 32 bits.

所以你可以看到里面发生了什么。在你的情况下,第一行你可以得到1 2和3 4和5 6的总和,然后继续。在第二行,你得到1 2 3 4和5 6 7 8的总和,然后继续。所以通过这样做5次,你得到所有32位的数量。

#4


1  

The first line treats the integer as an array of 16 2-bit integers, the result of the expression is 0 if 0 bits in the 2-bit pair were set, 1 if 1 bit in the 2-bit pair was set (01 bin or 10 bin), and 2 if both bits of the 2 bit pair were set. This is because we consider the lower of every two bits of x, and the lower of every two bits of x shifted right by one; adding them together. We know no carries will occur outside of the 2-bit pairs because our summands are limited to 0 or 1. The result is then stored back into x.

第一行将整数视为16个2位整数的数组,如果设置了2位对中的0位,则表达式的结果为0;如果设置了2位对中的1位,则表达式为1(01 bin或者10 bin),如果设置了2位对的两个位,则为2。这是因为我们考虑x的每两位中的较低位,并且x的每两位中的较低位右移一位;将它们加在一起。我们知道在2位对之外不会发生进位,因为我们的求和被限制为0或1.然后将结果存储回x。

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

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

The next line does the same thing, this time treating every 2-bits as a separate integer, storing their sum in every 4-bits the two summands used to occupy. After this operation the integer essentially becomes an array of 8 4-bit integers. Before the operation each summand is either 0, 1, or 2 (decimal) because it corresponds to the counts from the last line. Because of this we know each sum will be no greater than 4. Since each 4-bit int has a maximum value of 15 (decimal), we know this will also not overflow. As above, the result is stored back into x.

下一行做同样的事情,这次将每2位作为一个单独的整数处理,将它们的总和存储在用于占用的两个加数的每4位中。在此操作之后,整数基本上变为8个4位整数的数组。在操作之前,每个summand是0,1或2(十进制),因为它对应于最后一行的计数。因此我们知道每个总和不会大于4.因为每个4位int的最大值为15(十进制),我们知道这也不会溢出。如上所述,结果存储回x。

x = (x & (0x33333333)) + ((x >> 2) & (0x33333333));

x =(x&(0x33333333))+((x >> 2)&(0x33333333));

We do the same as above, this time suming every pair of 4-bits into every set of 8 bits.

我们也是如上所述,这次将每对4位加到每组8位中。

x = (x & (0x0f0f0f0f)) + ((x >> 4) & (0x0f0f0f0f));

x =(x&(0x0f0f0f0f))+((x >> 4)&(0x0f0f0f0f));

We then sum ever pair of 8-bits into every pair of 16 bits (the upper and lower half of x).

然后,我们将每对16位(x的上半部分和下半部分)中的一对8位相加。

x = (x & (0x00ff00ff)) + ((x >> 8) & (0x00ff00ff));

x =(x&(0x00ff00ff))+((x >> 8)&(0x00ff00ff));

We now sum the upper and lower half of x, and we are left with a 32-bit value equal to the number of bits set in the value of x.

我们现在将x的上半部分和下半部分相加,并且我们留下32位值,该值等于x值中设置的位数。

x = (x & (0x0000ffff)) + ((x >> 16) & (0x0000ffff));

x =(x&(0x0000ffff))+((x >> 16)&(0x0000ffff));

The key here is that each step does an inplace pairwise count of bits until we are left with the total bit count in the 32-bit integer it self.

这里的关键是每一步都进行一个就位的成对位计数,直到我们留下32位整数的总位数。