C 位旋转:设置了哪个位?

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

我有一个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;
while ((value & 1) ==0)
{
   ++bit_position;
   value >>= 1;
}
然后根据您所说的位位置查找素数。

您可能会发现log(n)/log(2)给出了0,1,2。。。你在一个合理的时间范围内。否则,某种形式的基于哈希表的方法可能会很有用。

有些体系结构(实际上是一个惊人的数字)只有一条指令可以执行您想要的计算。在ARM上,它将是
CLZ
(计数前导零)指令。对于intel,BSF(位向前扫描)或BSR(位反向扫描)指令将帮助您解决此问题

我想这不是一个真正的C答案,但它会让你的速度你需要


  • 预计算1如果性能是一个严重的问题,那么您应该使用内部函数/内置函数来使用特定于CPU的指令,例如下面针对GCC的指令:

    • 内置函数
      int\u内置ffs(无符号int x)

      返回1加上x的最低有效1位的索引,或者如果x为零,则返回零

    • 内置函数
      int\uu内置函数(无符号int x)

      返回x中前导0位的数目,从最高有效位位置开始。如果x为0,则结果未定义

    • 内置函数
      int\uu内置函数(未签名的int x)

      返回x中从最低有效位位置开始的尾随0位数。如果x为0,则结果未定义

    这样的事情是许多O(1)算法的核心,例如内核调度器,它需要找到由位数组表示的第一个非空队列


    注意:我列出了
    无符号int
    版本,但GCC也有
    无符号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
    被假定为
    uint64\u t
    ,但要对32位机器进行优化,您应该首先进行特殊情况检查,然后使用32位
    val
    变量执行循环。

    具体请参见“查找整数的整数日志基数2(即最高位集的位置)”-对于一些替代算法SM。(如果你真的对速度很感兴趣,如果CPU有一个专用指令,你可以考虑开除C)。

    < P>另一个回答,假设IEEE <代码>浮点< /代码>:

    int get_bit_index(uint64_t val)
    {
        union { float f; uint32_t i; } u = { val };
        return (u.i>>23)-127;
    }
    

    它按照您要求的输入值(正好是1位集)的指定工作,并且对其他值也有有用的行为(尝试准确地找出该行为是什么)。不知道是快还是慢;这可能取决于您的机器和编译器。

    调用glibc中的gnuposix扩展函数。如果功能不存在,请依靠。这两个函数都返回第一个位集的
    索引+1
    ,或零。使用Visual C++,您可以使用。

    最终找到最佳解决方案。请参阅本节末尾,了解在保证输入正好有一个非零位时应采取的措施:

    代码如下:

    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];
    
    您可以将其应用于基于直接乘法的64位输入算法;否则,只需添加一个条件,查看位是在上32位还是在下32位,然后在此使用32位算法

    更新:这里至少有一个我自己开发的64位版本,但它使用除法(实际上是模)

    对于2的每一次幂,
    v%67
    都有一个不同的值,因此只需将奇数素数(或位索引,如果不需要奇数素数的话)放在表中的正确位置即可。不使用3个位置(0、17和34),如果您还希望接受所有位0作为输入,这可能会很方便

    更新2:64位版本。

    r = Table[(uint64_t)(val * 0x022fdd63cc95386dull) >> 58];
    
    这是我的原创作品,但我从中获得了
    B(2,6)
    ,所以除了弄清楚debruijn序列是什么和使用谷歌之外,我什么都不能相信

    关于如何工作的一些补充说明:

    幻数是a
    B(2,6)
    De Bruijn序列。它的特性是,如果您查看一个连续的6位窗口,您可以通过适当旋转数字来获得该窗口中的任何6位值,并且每个可能的6位值都是通过一次旋转获得的

    我们将讨论中的窗口固定为前6位位置,并选择一个在前6位中有0的De Bruijn序列。这使得我们不必处理位旋转,只需移位,因为0将自然地进入底部位(并且我们永远不会看到超过5位f)
    r = Table[(uint64_t)(val * 0x022fdd63cc95386dull) >> 58];
    
    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; }
    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.