C 位旋转:设置了哪个位?
我有一个64位无符号整数,正好是1位。我想为可能的64个值中的每一个分配一个值(在本例中,奇数素数,因此0x1对应于3,0x2对应于5,…,0x80000000000000对应于313) 看起来最好的方法是转换1→ 0, 2 → 1, 4 → 2, 8 → 3, …, 263 → 63并在数组中查找值。但即使是这样,我也不确定获得二进制指数的最快方法是什么。也许还有更有效的方法C 位旋转:设置了哪个位?,c,bit-manipulation,C,Bit Manipulation,我有一个64位无符号整数,正好是1位。我想为可能的64个值中的每一个分配一个值(在本例中,奇数素数,因此0x1对应于3,0x2对应于5,…,0x80000000000000对应于313) 看起来最好的方法是转换1→ 0, 2 → 1, 4 → 2, 8 → 3, …, 263 → 63并在数组中查找值。但即使是这样,我也不确定获得二进制指数的最快方法是什么。也许还有更有效的方法 此操作将使用1014到1016次,因此性能是一个严重的问题 unsigned bit_position = 0; wh
此操作将使用1014到1016次,因此性能是一个严重的问题
unsigned bit_position = 0;
while ((value & 1) ==0)
{
++bit_position;
value >>= 1;
}
然后根据您所说的位位置查找素数。您可能会发现log(n)/log(2)给出了0,1,2。。。你在一个合理的时间范围内。否则,某种形式的基于哈希表的方法可能会很有用。有些体系结构(实际上是一个惊人的数字)只有一条指令可以执行您想要的计算。在ARM上,它将是CLZ
(计数前导零)指令。对于intel,BSF(位向前扫描)或BSR(位反向扫描)指令将帮助您解决此问题
我想这不是一个真正的C答案,但它会让你的速度你需要 - 预计算1如果性能是一个严重的问题,那么您应该使用内部函数/内置函数来使用特定于CPU的指令,例如下面针对GCC的指令:
- 内置函数
返回1加上x的最低有效1位的索引,或者如果x为零,则返回零int\u内置ffs(无符号int x)
- 内置函数
返回x中前导0位的数目,从最高有效位位置开始。如果x为0,则结果未定义int\uu内置函数(无符号int x)
- 内置函数
返回x中从最低有效位位置开始的尾随0位数。如果x为0,则结果未定义int\uu内置函数(未签名的int x)
注意:我列出了
版本,但GCC也有无符号int
版本。您可以使用二进制搜索技术:无符号long
int pos = 0; if ((value & 0xffffffff) == 0) { pos += 32; value >>= 32; } if ((value & 0xffff) == 0) { pos += 16; value >>= 16; } if ((value & 0xff) == 0) { pos += 8; value >>= 8; } if ((value & 0xf) == 0) { pos += 4; value >>= 4; } if ((value & 0x3) == 0) { pos += 2; value >>= 2; } if ((value & 0x1) == 0) { pos += 1; }
这比循环的优点是循环已经展开。但是,如果这真的是性能关键,您将需要测试和度量每个建议的解决方案。除了使用汇编或编译器特定的扩展来查找设置的第一位/最后一位之外,最快的算法是二进制搜索。首先检查是否设置了前32位中的任何一位。如果是,请检查是否设置了前16项中的任何一项。如果是,请检查是否设置了前8项中的任何一项。这样做的函数可以在搜索的每个叶直接返回一个奇数素数,也可以返回一个位索引,作为奇数素数表的数组索引 下面是二进制搜索的循环实现,如果认为这是最佳的,编译器当然可以展开它:uint32_t mask=0xffffffff; int pos=0, shift=32, i; for (i=6; i; i--) { if (!(val&mask)) { val>>=shift; pos+=shift; } shift>>=1; mask>>=shift; }
被假定为val
,但要对32位机器进行优化,您应该首先进行特殊情况检查,然后使用32位uint64\u t
变量执行循环。具体请参见“查找整数的整数日志基数2(即最高位集的位置)”-对于一些替代算法SM。(如果你真的对速度很感兴趣,如果CPU有一个专用指令,你可以考虑开除C)。 < P>另一个回答,假设IEEE <代码>浮点< /代码>:val
int get_bit_index(uint64_t val) { union { float f; uint32_t i; } u = { val }; return (u.i>>23)-127; }
它按照您要求的输入值(正好是1位集)的指定工作,并且对其他值也有有用的行为(尝试准确地找出该行为是什么)。不知道是快还是慢;这可能取决于您的机器和编译器。调用glibc中的gnuposix扩展函数。如果功能不存在,请依靠。这两个函数都返回第一个位集的
,或零。使用Visual C++,您可以使用。最终找到最佳解决方案。请参阅本节末尾,了解在保证输入正好有一个非零位时应采取的措施: 代码如下:索引+1
您可以将其应用于基于直接乘法的64位输入算法;否则,只需添加一个条件,查看位是在上32位还是在下32位,然后在此使用32位算法 更新:这里至少有一个我自己开发的64位版本,但它使用除法(实际上是模) 对于2的每一次幂,static const int MultiplyDeBruijnBitPosition2[32] = { 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 }; r = MultiplyDeBruijnBitPosition2[(uint32_t)(v * 0x077CB531U) >> 27];
都有一个不同的值,因此只需将奇数素数(或位索引,如果不需要奇数素数的话)放在表中的正确位置即可。不使用3个位置(0、17和34),如果您还希望接受所有位0作为输入,这可能会很方便 更新2:64位版本。v%67
这是我的原创作品,但我从中获得了r = Table[(uint64_t)(val * 0x022fdd63cc95386dull) >> 58];
,所以除了弄清楚debruijn序列是什么和使用谷歌之外,我什么都不能相信 关于如何工作的一些补充说明: 幻数是aB(2,6)
De Bruijn序列。它的特性是,如果您查看一个连续的6位窗口,您可以通过适当旋转数字来获得该窗口中的任何6位值,并且每个可能的6位值都是通过一次旋转获得的 我们将讨论中的窗口固定为前6位位置,并选择一个在前6位中有0的De Bruijn序列。这使得我们不必处理位旋转,只需移位,因为0将自然地进入底部位(并且我们永远不会看到超过5位f)B(2,6)
unsigned char leadz (BitBoard b) /************************************************************************** * * Returns the leading bit in a bitboard. Leftmost bit is 0 and * rightmost bit is 63. Thanks to Robert Hyatt for this algorithm. * ***************************************************************************/ { if (b >> 48) return lzArray[b >> 48]; if (b >> 32) return lzArray[b >> 32] + 16; if (b >> 16) return lzArray[b >> 16] + 32; return lzArray[b] + 48; }r = Table[(uint64_t)(val * 0x022fdd63cc95386dull) >> 58];
static inline unsigned char bit_offset(unsigned long long self) { static const unsigned char mapping[64] = { [0]=0, [1]=1, [2]=2, [4]=3, [8]=4, [17]=5, [34]=6, [5]=7, [11]=8, [23]=9, [47]=10, [31]=11, [63]=12, [62]=13, [61]=14, [59]=15, [55]=16, [46]=17, [29]=18, [58]=19, [53]=20, [43]=21, [22]=22, [44]=23, [24]=24, [49]=25, [35]=26, [7]=27, [15]=28, [30]=29, [60]=30, [57]=31, [51]=32, [38]=33, [12]=34, [25]=35, [50]=36, [36]=37, [9]=38, [18]=39, [37]=40, [10]=41, [21]=42, [42]=43, [20]=44, [41]=45, [19]=46, [39]=47, [14]=48, [28]=49, [56]=50, [48]=51, [33]=52, [3]=53, [6]=54, [13]=55, [27]=56, [54]=57, [45]=58, [26]=59, [52]=60, [40]=61, [16]=62, [32]=63 }; return mapping[((self & -self) * 0x022FDD63CC95386DULL) >> 58]; }
>>> ', '.join('[{0}]={1}'.format(((2**bit * 0x022fdd63cc95386d) % 2**64) >> 58, bit) for bit in xrange(64)) '[0]=0, [1]=1, [2]=2, [4]=3, [8]=4, [17]=5, [34]=6, [5]=7, [11]=8, [23]=9, [47]=10, [31]=11, [63]=12, [62]=13, [61]=14, [59]=15, [55]=16, [46]=17, [29]=18, [58]=19, [53]=20, [43]=21, [22]=22, [44]=23, [24]=24, [49]=25, [35]=26, [7]=27, [15]=28, [30]=29, [60]=30, [57]=31, [51]=32, [38]=33, [12]=34, [25]=35, [50]=36, [36]=37, [9]=38, [18]=39, [37]=40, [10]=41, [21]=42, [42]=43, [20]=44, [41]=45, [19]=46, [39]=47, [14]=48, [28]=49, [56]=50, [48]=51, [33]=52, [3]=53, [6]=54, [13]=55, [27]=56, [54]=57, [45]=58, [26]=59, [52]=60, [40]=61, [16]=62, [32]=63'
>>> ', '.join(map(str, {((2**bit * 0x022fdd63cc95386d) % 2**64) >> 58: bit for bit in xrange(64)}.values())) '0, 1, 2, 53, 3, 7, 54, 27, 4, 38, 41, 8, 34, 55, 48, 28, 62, 5, 39, 46, 44, 42, 22, 9, 24, 35, 59, 56, 49, 18, 29, 11, 63, 52, 6, 26, 37, 40, 33, 47, 61, 45, 43, 21, 23, 58, 17, 10, 51, 25, 36, 32, 60, 20, 57, 16, 50, 31, 19, 15, 30, 14, 13, 12'
unsigned char bit_offset(unsigned long long self) { static const unsigned char table[64] = { 0, 1, 2, 53, 3, 7, 54, 27, 4, 38, 41, 8, 34, 55, 48, 28, 62, 5, 39, 46, 44, 42, 22, 9, 24, 35, 59, 56, 49, 18, 29, 11, 63, 52, 6, 26, 37, 40, 33, 47, 61, 45, 43, 21, 23, 58, 17, 10, 51, 25, 36, 32, 60, 20, 57, 16, 50, 31, 19, 15, 30, 14, 13, 12 }; return table[((self & -self) * 0x022FDD63CC95386DULL) >> 58]; }
>>> table = {((2**bit * 0x022fdd63cc95386d) % 2**64) >> 58: bit for bit in xrange(64)}.values() >>> assert all(i == table[(2**i * 0x022fdd63cc95386d % 2**64) >> 58] for i in xrange(64))
static public final int msb(int n) { n |= n >>> 1; n |= n >>> 2; n |= n >>> 4; n |= n >>> 8; n |= n >>> 16; n >>>= 1; n += 1; return n; } static public final int msb_index(int n) { final int[] multiply_de_bruijn_bit_position = { 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 }; return multiply_de_bruijn_bit_position[(msb(n) * 0x077CB531) >>> 27]; }
// Count the consecutive zero bits (trailing) on the right with multiply and lookup unsigned int v; // find the number of trailing zeros in 32-bit v int r; // result goes here static const int MultiplyDeBruijnBitPosition[32] = { 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 }; r = MultiplyDeBruijnBitPosition[((uint32_t)((v & -v) * 0x077CB531U)) >> 27]; // Converting bit vectors to indices of set bits is an example use for this. // It requires one more operation than the earlier one involving modulus // division, but the multiply may be faster. The expression (v & -v) extracts // the least significant 1 bit from v. The constant 0x077CB531UL is a de Bruijn // sequence, which produces a unique pattern of bits into the high 5 bits for // each possible bit position that it is multiplied against. When there are no // bits set, it returns 0. More information can be found by reading the paper // Using de Bruijn Sequences to Index 1 in a Computer Word by // Charles E. Leiserson, Harald Prokof, and Keith H. Randall.
- 内置函数