高效的按位运算,用于计数位或查找最右|最左的位

发布于 2024-12-31 22:18:12 字数 385 浏览 1 评论 0原文

给定一个 unsigned int,我必须实现以下操作:

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

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

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

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

其他操作类似。

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

Given an unsigned int, I have to implement the following operations :

  1. Count the number of bits set to 1
  2. Find the index of the left-most 1 bit
  3. Find the index of the righ-most 1 bit

(the operation should not be architecture dependents).

I've done this using bitwise shift, but I have to iterate through almost all the bits(es.32) .
For example, counting 1's:

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

The others operation are similar.

So my question is: is there any faster way to do that?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

梦行七里 2025-01-07 22:18:12

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

Windows/MSVC:

GCC:

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

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

If you want the fastest way, you will need to use non-portable methods.

Windows/MSVC:

GCC:

These typically map directly to native hardware instructions. So it doesn't get much faster than these.

But since there's no C/C++ functionality for them, they're only accessible via compiler intrinsics.

箹锭⒈辈孓 2025-01-07 22:18:12

看一下 ffs(3)、ffsl(3)、fls(3)、flsl(3)。

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

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

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

Take a look at ffs(3), ffsl(3), fls(3), flsl(3).

The ffs() and ffsl() functions find the first bit set (beginning with the least significant bit) in i and return the index of that bit.

The fls() and flsl() functions find the last bit set in i and return the index of that bit.

You might be interested in bitstring(3), too.

权谋诡计 2025-01-07 22:18:12

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

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

unsigned int v; // 计数在此设置的位数(32 位值)
无符号整型 c; // 在这里存储总数
v = v - ((v >> 1) & 0x55555555); // 重用输入作为临时输入
v = (v & 0x33333333) + ((v >> 2) & 0x33333333); // 温度
c = ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >>> 24; // 数数

最好的位计数方法只需要 12 次操作,这与查找表方法相同,但避免了表的内存和潜在的缓存未命中。它是上面纯并行方法和早期使用乘法的方法(在使用 64 位指令计算位数的部分)之间的混合体,尽管它不使用 64 位指令。字节中设置的位数的计数是并行完成的,字节中设置的位数的总和是通过乘以 0x1010101 并右移 24 位来计算的。

Quoting from http://graphics.stanford.edu/~seander/bithacks.html

The best method for counting bits in a 32-bit integer v is the following:

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

The best bit counting method takes only 12 operations, which is the same as the lookup-table method, but avoids the memory and potential cache misses of a table. It is a hybrid between the purely parallel method above and the earlier methods using multiplies (in the section on counting bits with 64-bit instructions), though it doesn't use 64-bit instructions. The counts of bits set in the bytes is done in parallel, and the sum total of the bits set in the bytes is computed by multiplying by 0x1010101 and shifting right 24 bits.

终难愈 2025-01-07 22:18:12

数字 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。因此,从对数结果中减去 1,您将得到最右边的位置‘1’位。

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

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

"The righ-most 1 bit" of number x is given by

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

E.g.:

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

The base2-log of the last line then gives you the correct position + 1. So substract 1 from the log-result and you'll have the most-right '1' bit.

For the most-right '0' bit you may use

pos(1st '0') = log_2(x XOR (x+1) + 1) - 1
夏末染殇 2025-01-07 22:18:12

使用 C++20,可以使用新添加的标头 <位>

  1. popcount
  2. countl_one
  3. countr_one

然后,它们中的每一个都会调用编译器特定的函数,例如__popcnt()__builtin_popcount()

With C++20, these operations can be done easily with the newly added header, <bit>.

  1. popcount
  2. countl_one
  3. countr_one

Each of them would then call compiler specific functions like __popcnt() and __builtin_popcount().

坏尐絯℡ 2025-01-07 22:18:12

对于最右边的一点简单的答案

第一种方法

unsigned int getFirstSetBit(int n){

return log2(n & -n) + 1; 

}

第二种方法

unsigned int getFirstSetBit(int n){

return ffs(n);   

}

for rightmost bit simple ans

First Method

unsigned int getFirstSetBit(int n){

return log2(n & -n) + 1; 

}

Second Method

unsigned int getFirstSetBit(int n){

return ffs(n);   

}

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文