最低位的索引

时间:2009-09-25 15:31:59

标签: c binary

我想找到获得long long最低位的索引的最快方法。即:

00101001001000 -> 3

涉及循环和移位的解决方案太慢了。即:

int i;
if(bits == 0ULL) {
  i = 64;
} else {
  for(i = 0;!(bits & 1ULL);i++)
    bits >>= 1;
}

编辑:使用信息

使用ffsll的函数不能真正减少它的用法,但在这里(当然简化)。它只是遍历索引并对它们做了一些事情。这个函数可能是我整个应用程序中使用最广泛的函数,尽管有很多缓存它的价值。它是alpha-beta搜索引擎中的合法移动生成器。

while(bits){
  index = ffsll(bits);
  doSomething(index);
  index &= index-1;
}

11 个答案:

答案 0 :(得分:11)

英特尔有专门的指令来查找最低或最高阶设置位。 BSF似乎就像你需要的那样。至于在普通C中这样做,也许bit twiddling hacks page有你需要的东西。

至少你可以使用半字节或字节表来加快速度。像这样的东西(为int演示,但根据需要可以很容易地改变为longlong。)

/*
0000 - 0
0001 - 1
0010 - 2
0011 - 1
0100 - 3
0101 - 1
0110 - 2
0111 - 1
1000 - 4
1001 - 1
1010 - 2
1011 - 1
1100 - 3
1101 - 1
1110 - 2
1111 - 1
*/

int ffs(int i) {
    int ret = 0;
    int j = 0;
    static const int _ffs_tab[] = 
        { 0, 1, 2, 1, 3, 1, 2, 1, 4, 1, 2, 1, 3, 1, 2, 1 };

    while((i != 0) && (ret == 0)) {
        ret = _ffs_tab[i & 0x0f];

        if(ret > 0) {
            break;
        }

        i >>= 4;
        j += 4;

        /* technically the sign bit could stay, so we mask it out to be sure */
        i &= INT_MAX;
    }

    if(ret != 0) {
        ret += j;
    }

    return ret;
}

答案 1 :(得分:9)

我发现的最快的是string.h中的ffsll(long long)

答案 2 :(得分:4)

如果使用Visual Studio,_BitScanForward

对于gcc,请尝试__builtin_ctz__builtin_ffs

与往常一样,应该查阅生成的代码以确保生成正确的指令。

答案 3 :(得分:3)

您可以使用x & (~x + 1)隔离最低设置位;这为您提供了最低位值,而不是索引(例如,如果x = 01101000,则结果为00001000)。我知道从那里到索引的最快方法可能是一个switch语句:

switch(x & (~x + 1))
{
  case     0ULL: index = -1; break;
  case     1ULL: index =  0; break;
  case     2ULL: index =  1; break;
  case     4ULL: index =  2; break;
  ...
  case 9223372036854775808ULL: index = 63; break;
}

丑陋,但没有涉及循环。

答案 4 :(得分:2)

如何实现一种二分查找?

查看由比特产生的低位和掩码值,这些值都是低半部分。如果该值为零,则您知道最小位位于数字的上半部分。

其他人明智地将事情减半,然后再去。

答案 5 :(得分:2)

这可能适用于32位。应该很容易扩展到64。

// all bits left of lsb become 1, lsb & right become 0
y = x ^ (-x);

// XOR a shifted copy recovers a single 1 in the lsb's location
u = y ^ (y >> 1);

// .. and isolate the bit in log2 of number of bits
i0 = (u & 0xAAAAAAAA) ?  1 : 0;
i1 = (u & 0xCCCCCCCC) ?  2 : 0;
i2 = (u & 0xF0F0F0F0) ?  4 : 0;
i3 = (u & 0xFF00FF00) ?  8 : 0;
i4 = (u & 0xFFFF0000) ? 16 : 0;
index = i4 | i3 | i2 | i1 | i0;

显然,如果有某种方法让硬件这样做,即,如果有特殊的CPU指令可用,那么就是这样。

答案 6 :(得分:0)

这样的事情怎么样?它大大减少了循环次数。

int shifts = 0;
if ((bits & 0xFFFFFFFFFFFFULL) == 0) // not in bottom 48 bits
{
    shifts = 48;
}
else if ((bits & 0xFFFFFFFFFFULL == 0) // not in bottom 40 bits
{
    shifts = 40;
}
else
// etc

bits >>= shifts;  // do all the shifts at once

// this will loop at most 8 times
for(i = 0;!(bits & 1ULL);i++)
    bits >>= 1;

index = shifts + i;

答案 7 :(得分:0)

我写了两个函数,它们返回与ffsll()相同的结果。

int func1( uint64_t n ){
  if( n == 0 ) return 0;
  n ^= n-1;
  int i = 0;
  if( n >= 1ull<<32 ){ n>>=32; i+=32; }
  if( n >= 1ull<<16 ){ n>>=16; i+=16; }
  if( n >= 1ull<< 8 ){ n>>= 8; i+= 8; }
  if( n >= 1ull<< 4 ){ n>>= 4; i+= 4; }
  if( n >= 1ull<< 2 ){ n>>= 2; i+= 2; }
  if( n >= 1ull<< 1 ){         i+= 1; }
  return i+1;
}

int func2( uint64_t n ){
  return n? ((union ieee754_float)((float)(n^(n-1)))).ieee.exponent-126: 0;
}

我不知道哪个是最快的:ffsll(),func1()或func2()?

答案 8 :(得分:0)

这是两个实现,首先是内在函数/汇编,第二个是c / c ++(索引从0开始)

unsigned int bsf_asm(unsigned int b)
{

    // b == 0 is undefined

#if defined( \__GNUC__ )

    return __builtin_ctz(b);

#else

    __asm bsf eax, b;

#endif

}


unsigned int bsf(unsigned int b)
{

    // b == 0 is undefined

    static const unsigned char btal[] = {0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0};

    int i = 0;
    if(!(b & 0x0000ffff))
    {
        b>>=16;
        i+=16;
    }

    if(!(b & 0x000000ff))
    {
        b>>=8;
        i+=8;
    }

    if(!(b & 0x0000000f))
    {
        b>>=4;
        i+=4;
    }

    return i+btal[b&0x0f];

}

答案 9 :(得分:-1)

要获得最右侧的设置位,可以使用以下表达式

将变量视为X

x&amp; 〜(x - 1)给出一个二进制数,其中只包含设置位,其余全部为零

示例

x      = 0101
x-1    = 0100
~(x-1) = 1011

x & ~ (x - 1) = 0100

现在不断将这个二进制数转移到右边,直到数字为零,并计算给出最右设置位数的移位数。

答案 10 :(得分:-3)

如果您的号码是奇数或偶数,您可以先将算法检查的复杂性减半。 如果它是偶数,则最低位是第一个。

对于奇怪的情况,你可以实现这样的二元搜索...