对32位整数中的设置位进行计数的方法的说明

Explanation of a method of counting set bits in a 32-bit integer

本文关键字:说明 方法 置位 32位 整数      更新时间:2023-10-16

查看此链接中的集合位计数方法,我发现以下方法

作者说:

对32位整数v中的位进行计数的最佳方法是以下内容:

v = v - ((v >> 1) & 0x55555555);                    // reuse input as temporary
v = (v & 0x33333333) + ((v >> 2) & 0x33333333);     // temp
c = ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24; // count

最好的比特计数方法只需要12次操作,即与查找表方法相同,但避免了内存和电势表的缓存未命中。它是纯平行之间的混合上面的方法和前面使用乘法的方法(在一节中关于使用64位指令计数位(,尽管它不使用64位指令。字节中设置的位数在中完成并行,并且计算字节中设置的位的总和通过乘以0x1010101并右移24位。

有没有解释一下这个方法是如何计算集合位的?

它之所以有效,是因为您可以通过一分为二、计算两半的集合位数,然后将其相加来计算集合位数的总数。也称为Divide and Conquer范式。让我们详细介绍一下。。

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

两个比特中的比特数量可以是0b000b010b10。让我们试着用2位来计算这个。。

 ---------------------------------------------
 |   v    |   (v >> 1) & 0b1010   |   v - x   |
 ---------------------------------------------
   0b00           0b00               0b00   
   0b01           0b00               0b01     
   0b10           0b01               0b01
   0b11           0b01               0b10

这就是所需要的,最后一列显示了每两位对中设置位的计数。如果两位数是>= 2 (0b10),则and产生0b01,否则产生0b00

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

这句话应该很容易理解。在第一次运算之后,我们每两个比特中有一个集合比特的计数,现在我们每4个比特中的计数相加。

v & 0b11001100         //masks out even two bits
(v >> 2) & 0b11001100  // masks out odd two bits

然后,我们将上述结果相加,得到4位中的集合位的总数。最后一句话是最棘手的。

c = ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24;

让我们进一步打破它。。

v + (v >> 4)

它类似于第二条语句,我们以4为一组来计算集合位。我们知道,由于我们之前的操作,每个半字节都有设置位的计数。让我们看一个例子,假设我们有字节0b01000010。这意味着第一个半字节有4比特集,而第二个半字节则有2比特集。现在我们把这些小点心加在一起。

0b01000010 + 0b01000000

它给出了第一个半字节0b01100010中一个字节中设置位的计数,因此我们屏蔽了该数字中所有字节的最后四个字节(丢弃它们(。

0b01100010 & 0xF0 = 0b01100000

现在,每个字节都有集合位的计数。我们需要把它们加在一起。诀窍是将结果乘以具有有趣性质的0b10101010。如果我们的数字有四个字节A B C D,它将产生一个带有这些字节A+B+C+D B+C+D C+D D的新数字。一个4字节的数字最多可以设置32位,可以表示为0b00100000

我们现在只需要第一个字节,它是所有字节中所有设置位的总和,我们通过>> 24得到它。该算法是为32 bit字设计的,但可以很容易地为64 bit字修改。