繁体   English   中英

用于计算位或找到最右边|最左边的位的高效按位运算

[英]Efficient bitwise operations for counting bits or find the right|left most ones

给定一个 unsigned int,我必须执行以下操作:

  1. 计算设置为 1 的位数
  2. 找到最左边1位的索引
  3. 找到最右1位的索引

(该操作不应依赖于体系结构)。

我已经使用位移位完成了此操作,但我必须遍历几乎所有位(es.32)。 例如,计算 1:

unsigned int number= ...;
while(number != 0){
    if ((number & 0x01) != 0)
        ++count;
    number >>=1;
}

其他操作类似。

所以我的问题是:有没有更快的方法来做到这一点?

如果您想要最快的方式,您将需要使用不可移植的方法。

视窗/MSVC:

海湾合作委员会:

这些通常直接映射到本机硬件指令。 所以它不会比这些更快。

但是由于它们没有 C/C++ 功能,因此只能通过编译器内部函数访问它们。

看看 ffs(3), ffsl(3), fls(3), flsl(3)。

ffs() 和 ffsl() 函数查找 i 中的第一个位集(从最低有效位开始)并返回该位的索引。

fls() 和 flsl() 函数查找 i 中设置的最后一位并返回该位的索引。

您可能也对 bitstring(3) 感兴趣。

引自http://graphics.stanford.edu/~seander/bithacks.html

对 32 位整数 v 中的位进行计数的最佳方法如下:

 unsigned int v; // count bits set in this (32-bit value) unsigned int c; // store the total here 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 位来计算的。

数字 x 的“最右边的 1 位”由下式给出

pos(1st '1') = log_2(x XOR (x-1) + 1) - 1

例如:

x               = 1100101000;
x-1             = 1100100111;
x XOR (x-1)     = 0000001111;
x XOR (x-1) + 1 = 0000010000;

最后一行的 base2-log 为您提供正确的位置 + 1。因此,从 log-result 中减去 1,您将获得最正确的“1”位。

对于最右边的“0”位,您可以使用

pos(1st '0') = log_2(x XOR (x+1) + 1) - 1

在 C++20 中,这些操作可以通过新添加的 header, <bit>轻松完成。

  1. popcount
  2. countl_one
  3. countr_one

然后他们每个人都会调用编译器特定的函数,如__popcnt()__builtin_popcount()

一种方法是使用查找表。

uint8_t popcount_table[256] = { ... };

uint8_t popcount (uint32_t x)
{
    uint8_t *p = (uint8_t*)&x;

    return popcount_table[p[0]] +
           popcount_table[p[1]] +
           popcount_table[p[2]] +
           popcount_table[p[3]];
}

对于最右边的简单答案

第一种方法

unsigned int getFirstSetBit(int n){

return log2(n & -n) + 1; 

}

第二种方法

unsigned int getFirstSetBit(int n){

return ffs(n);   

}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM