查看此link中计数设置位的方法,我找到了以下方法
作者说:
计算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位。
任何解释这个方法如何计算设置位?
答案 0 :(得分:4)
它的工作原理是因为您可以通过分成两半来计算设置位的总数,计算两半中的设置位数,然后将它们相加。也称为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个组中的设置位。我们知道,由于我们之前的操作,每个半字节都有其中的设置位数。让我们看一个例子,假设我们有字节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
字。