它之所以有效,是因为您可以通过分成两半来计算设置位的总数,计算两半中设置位的数量,然后将它们相加。也称为Divide and Conquer
范式。让我们详细了解一下。。
v = v - ((v >> 1) & 0x55555555);
两位中的位数可以是0b00
,0b01
或0b10
。让我们尝试在 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 个为一组来计算设置位。我们知道,由于我们之前的操作,每个半字节都有设置位的计数。让我们看一个例子,假设我们有 byte 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
单词进行修改。