连续计数1位的最快方法。 C ++

时间:2014-07-16 11:15:23

标签: c++ count bit-manipulation bit

基本上我只需要知道int或unsigned int中最高1位的位置。像:

00001111=4;
00011111=5;
11111111=8;

因为我确信我将获得的任何数字都将连续1位。 0 ... 0000011 ... 1将没有..00010011 ...或者其他东西。因此,方法可以找到最高1或仅计数1。不管。

这是我设法做的最好的事情:

Uint32 number;
int shift=16; int segment=8;
while (segment) 
{
if (number>>shift!=0) shift+=segment; 
else shift-=segment;
segment>>1; // /2
}

9 个答案:

答案 0 :(得分:3)

复制/粘贴我的功能:

size_t FirstSetBit(unsigned int v) const
{
#if defined(_MSC_VER)
    unsigned long ul;
    // Just 10% faster than MultiplyDeBruijnBitPosition method, on Core i7
    _BitScanForward(&ul, v);
    return ul;
#elif defined(__GNUC__) || defined(__clang__)
    return 31 - __builtin_clz(v);
#else // integer fallback for non-x64
    #warning You may be able to optimise this code for your compiler/processor

    int r;
    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 & -int(v)) * 0x077CB531U)) >> 27];
return r;
#endif
}

答案 1 :(得分:2)

假设您知道有问题的int的大小(例如,32位),您可以非常轻松地使用二进制搜索来设置最高位:

int bit_pos(unsigned value) { 
    static const std::vector<unsigned> masks = {
        0xffff0000, 
        0xff00ff00, 
        0xf0f0f0f0, 
        0xcccccccc, 
        0xaaaaaaaa
    };

    if (!value)
        return 0;

    int position = 0;

    int val = 16;

    for (unsigned mask : masks) {
        if (value & mask) {
            position += val;
            value &= mask;
        }
        val /= 2;
    }

    return position + 1;
}

为了(可能)以更大的默默无闻为代价获得一点额外的速度,你可以做一些额外的摆弄,只获得一个位,然后找到它的位置:

unsigned bit_pos2(unsigned int value) {
    unsigned int position = 32;
    value = ~value;
    value &= -signed(value);
    if (value) --position;
    if (value & 0x0000ffff) position -= 16;
    if (value & 0x00ff00ff) position -= 8;
    if (value & 0x0f0f0f0f) position -= 4;
    if (value & 0x33333333) position -= 2;
    if (value & 0x55555555) position -= 1;
    return position;
}

对于64位整数,数字会变大,但我们只需再添加一次迭代:

unsigned bit_pos64(unsigned long long value) {
    value = ~value;
    unsigned position = 64;
    value &= -(long long)value;
    if (value) --position;
    if (value & 0x00000000ffffffff) position -= 32;
    if (value & 0x0000ffff0000ffff) position -= 16;
    if (value & 0x00ff00ff00ff00ff) position -= 8;
    if (value & 0x0f0f0f0f0f0f0f0f) position -= 4;
    if (value & 0x3333333333333333) position -= 2;
    if (value & 0x5555555555555555) position -= 1;
    return position;
}

通过只设置一个位,我们避免了循环迭代之间的依赖关系,因此迭代可以并行执行。手动展开循环(如上所述)可能有助于发生这种情况的机会,至少是轻微的。这也需要每次迭代只需要一次操作而不是2次,所以即使没有任何并行执行它也可能更快。

答案 2 :(得分:2)

其他人给出了各种各样的答案,但值得一提的是,有一整本书充满了这些东西 - Hacker's Delight by Henry S. Warren Jr., ISBN 978-0-201-91465-8,其中也有a web page

值得强调的是,一些微处理器对某些类型的东西有特殊的支持。上面的Lasse Reinhold的答案利用了这一事实,但没有引起读者的注意。一般来说,除了非常简单的情况(如位旋转指令)之外,编译器无法将“bit twiddling”算法优化为单个机器指令,因此如果您知道自己位于具有例如一个机器指令的机器上。一个位扫描转发或人口计数指令或类似指令,您可以使用它(通过编译器内在函数或asm语句),您可能希望这样做。

最后,由于问题开始时已经知道该数字的形式为0 ... 000111 ... 1,我将添加另一个选项,基于计算(并行)组的总和位:

uint32_t count_set_bits(uint32_t x) {
  x = ((x >> 1) & 0x55555555) + (x & 0x55555555);
  x = ((x >> 2) & 0x33333333) + (x & 0x33333333);
  x = ((x >> 4) & 0x0f0f0f0f) + (x & 0x0f0f0f0f);
  x = ((x >> 8) + x) & 0x00ff00ff);
  return (x >> 16) + x) & 0x0000ffff;
}

答案 3 :(得分:2)

你做的是bit scan。在你的情况下,它被称为反向扫描。它也是log base two算法的底线。

x86指令集有一条指令,bsr =位扫描反转,因为Intel 386.所以你应该尝试使用一个函数来调用指令。对于MSVC,您希望使用_BitScanReverse,GCC 31 -__builtin_clz(x)和ICC _bit_scan_reverse

我查看了来自MSVC和GCC的这些内在函数/内置函数的汇编输出,它们都生成了bsr指令。

英特尔Haswell处理器添加了lzcnt指令(并且AMD在巴塞罗那很早就添加了它)。这会计算前导零。它与31 - bsr相同(或等于bsr - 请参阅下面的警告)。您可以使用带有MSVC的__lzcnt调用它。但是,您应该收到警告,如果您在不支持lzcnt的处理器上执行此操作,will not crash and instead will use the bsr instruction

  

lzcnt的编码类似于bsr,如果lzcnt在不支持它的CPU上执行,例如Haswell之前的Intel CPU,它将执行bsr操作,而不是引发无效的指令错误。 / p>

如果您想在软件中执行BitScanReverse,有几种不同的方法。请参阅&#34;找到N位整数的对数基数2&#34; http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogDeBruijnhttp://chessprogramming.wikispaces.com/BitScan

答案 4 :(得分:1)

(1)

你可以计算你将无符号整数移位到零之前需要多少次?

请参阅What are bitwise shift (bit-shift) operators and how do they work?

(2)

例如

号码:0111

向右移位一位:0011,使用按位x-或原始数字0111 ^ 0011 = 0100

在cpp:

unsigned int num = 3;

unsigned int answer = ((num >> 1) ^ (num)); 

cout << answer << '\n';

答案 5 :(得分:1)

int getHighestOne(unsigned int num) {
    int count = 0;
    while(num >>= 1) ++count;
    return count;
}

返回从0开始的最高位置,如果没有,则返回-1。

getHighestOne(0)将返回-1
getHighestOne(1)将返回0
getHighestOne(10)将返回3

修改 这是一些快速记录方法的link

答案 6 :(得分:1)

下面的代码可能有助于连续计数1位。

int count_consecutive_ones(int in) {
    int count = 0;
    while (in) {
        in = (in & (in << 1));
        count++;
    }
    return count;
}

答案 7 :(得分:0)

int count_bit(unsigned int num) {
    // Assume 0<= num < 256, i.e. 8 bit
    // It can be easily support larger word size by successive calling.
    if ( num >= 256 ) return -1;
    return count[ num ];
}

是的,最快的方法是查找表。

答案 8 :(得分:0)

你可以试试像

这样的东西
    std::bitset<32> b1; 
    int i_num;
    std::cin>> i_num;
    b1=i_num;
   if(b1[31]==1)
      return 31;
   else if(b1[30]==1)
      return 30;
     ---------
  else if(b1[0]==1)
 return 0;
 else
 reutrn -1;