Sean Eron Anderson的Bit Twiddling Hacks有这个技巧,其中包括:
计数位设置,并行
unsigned int v; // count bits set in this (32-bit value)
unsigned int c; // store the total here
static const int S[] = {1, 2, 4, 8, 16}; // Magic Binary Numbers
static const int B[] = {0x55555555, 0x33333333, 0x0F0F0F0F, 0x00FF00FF, 0x0000FFFF};
c = v - ((v >> 1) & B[0]);
c = ((c >> S[1]) & B[1]) + (c & B[1]);
c = ((c >> S[2]) + c) & B[2];
c = ((c >> S[3]) + c) & B[3];
c = ((c >> S[4]) + c) & B[4];
B数组,表示为二进制,是:
B[0] = 0x55555555 = 01010101 01010101 01010101 01010101
B[1] = 0x33333333 = 00110011 00110011 00110011 00110011
B[2] = 0x0F0F0F0F = 00001111 00001111 00001111 00001111
B[3] = 0x00FF00FF = 00000000 11111111 00000000 11111111
B[4] = 0x0000FFFF = 00000000 00000000 11111111 11111111
我们可以通过继续使用二进制幻数 B 和 S 的模式来调整更大整数大小的方法。如果有 k 位,那么我们需要数组 S 和 B 是 ceil(lg(k)) 个元素长,并且我们必须为 c 计算与 S 或 B 长的相同数量的表达式。对于 32 位 v,使用 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 位来计算字节中设置的位的总和。
将最佳位计数方法推广到位宽高达 128 的整数(由类型 T 参数化)是这样的:
v = v - ((v >> 1) & (T)~(T)0/3); // temp
v = (v & (T)~(T)0/15*3) + ((v >> 2) & (T)~(T)0/15*3); // temp
v = (v + (v >> 4)) & (T)~(T)0/255*15; // temp
c = (T)(v * ((T)~(T)0/255)) >> (sizeof(T) - 1) * CHAR_BIT; // count