识别64位位掩码中所有设置位位置的最佳方法是什么。假设我的位掩码是0xDeadBeefDeadBeef,那么识别其中所有位位置的最佳方法是什么。
long long bit_mask = 0xdeadbeefdeadbeef;
unsigned int bit_pos=0;
while(mask) {
if((mask&1)==1) {
printf("Set bit position is:%d \n",bit_pos};
}
bit_pos++;
mask>>=1;
}
一种方法是遍历它,并检查是否设置了一个位,如果它被设置,返回计数位置并继续循环直到MSB,所以对于64位,我会迭代直到我拥有所有遍历设置位或遍历所有64位,如果设置了MSB,但必须有更好的方法吗?
答案 0 :(得分:2)
来自Hacker's Delight的算法(书):
int count_bits(long long s)
{
s = (s&0x5555555555555555L) + ((s>>1)&0x5555555555555555L);
s = (s&0x3333333333333333L) + ((s>>2)&0x3333333333333333L);
s = (s&0x0F0F0F0F0F0F0F0FL) + ((s>>4)&0x0F0F0F0F0F0F0F0FL);
s = (s&0x00FF00FF00FF00FFL) + ((s>>8)&0x00FF00FF00FF00FFL);
s = (s&0x0000FFFF0000FFFFL) + ((s>>16)&0x0000FFFF0000FFFFL);
s = (s&0x00000000FFFFFFFFL) + ((s>>32)&0x00000000FFFFFFFFL);
return (int)s;
}
答案 1 :(得分:1)
除了已经解释过的有点笨拙的黑客之外,还有其他选择。
这假设你有x86(64),SSE4,gcc和使用-msse4开关编译你可以使用:
int CountOnesSSE4(unsigned int x)
{
return __builtin_popcount(x);
}
这将编译成单个popcnt指令。如果您需要快速代码,您可以在运行时实际检查SSE并使用最佳功能。
如果您希望数字的数量很少,那么这也可能很快(并且总是比通常的移位和比较循环更快):
int CountOnes(unsigned int x)
{
int cnt = 0;
while (x) {
x >>= ffs(x);
cnt++;
}
return cnt;
}
在x86上(即使没有SSE),ffs将编译为单指令(bsf),循环次数将取决于1的数量。
答案 2 :(得分:0)
您可以这样做:
long long bit_mask = 0xdeadbeefdeadbeef;
int i;
for (i = 0; i < (sizeof(long long) * 8); i++) {
int res = bit_mask & 1;
printf ("Pos %i is %i\n", i, res);
bit_mask >>= 1;
}
答案 3 :(得分:0)
这取决于您是希望清晰的代码还是非常快速的结果。我几乎总是在代码中选择清晰度,除非分析告诉我。为清楚起见,您可能会执行以下操作:
int count_bits(long long value) {
int n = 0;
while(value) {
n += (value & 1);
value >>= 1;
}
return n;
}
对于性能,您可能希望从X J的答案中调用count_bits。
int count_bits(long long s)
{
s = (s&0x5555555555555555L) + ((s>>1)&0x5555555555555555L);
s = (s&0x3333333333333333L) + ((s>>2)&0x3333333333333333L);
s = (s&0x0F0F0F0F0F0F0F0FL) + ((s>>4)&0x0F0F0F0F0F0F0F0FL);
s = (s&0x00FF00FF00FF00FFL) + ((s>>8)&0x00FF00FF00FF00FFL);
s = (s&0x0000FFFF0000FFFFL) + ((s>>16)&0x0000FFFF0000FFFFL);
s = (s&0x00000000FFFFFFFFL) + ((s>>32)&0x00000000FFFFFFFFL);
return (int)s;
}
这取决于你是否想要查看你的代码并对自己说,“是的,这是有意义的”或“我会接受那个人的话”。
我已经在堆栈溢出之前被调用了。有些人不同意。一些非常聪明的人选择复杂而非简单。我相信干净的代码是简单的代码。
如果性能需要它,请使用复杂性。如果没有,请不要。
另外,请考虑代码审查。当有人说“count_bits如何工作?”时,你会说什么?
答案 4 :(得分:0)
如果你正在计算那些你可以使用快速的黑客快乐解决方案,但查找表可以(不总是)更快。而且更容易理解。您可以预先准备一个表,例如深256个项,表示字节值0x00到0xFF
的计数0, //0x00
1, //0x01
1, //0x02
2, //0x03
1, //0x04
2, //0x05
2, //0x06
3, //0x07
...
构建该表的代码可能会使用每一位方法的慢速步骤。
虽然你可以将更大的数字分成几个字节
count = table8[number&0xFF]; number>>=8;
count += table8[number&0xFF]; number>>=8;
count += table8[number&0xFF]; number>>=8;
count += table8[number&0xFF]; number>>=8;
...
如果你有更多的内存,你可以通过代表更宽的数字使表更大,一个65536深的表,数字为0x0000到0xFFFF。
count = table16[number&0xFFFF]; number>>16;
count += table16[number&0xFFFF]; number>>16;
count += table16[number&0xFFFF]; number>>16;
count += table16[number&0xFFFF]; number>>16;
表是一种以牺牲内存消耗为代价使这样的事情变得更快的一般方法。您可以消耗的内存越多,您就可以预先计算(在编译时或编译之前),而不是实时计算。