【问题标题】:Is there a fast way to get the index of bit which equal 1 in a binary value?有没有一种快速的方法来获取二进制值中等于 1 的位索引?
【发布时间】:2022-01-06 03:57:24
【问题描述】:

我想以二进制格式获取等于 1 的索引,现在我使用这样的代码:

inline static uint8_t the_index(uint32_t val){
  return uint8_t(log(val & ((~val) + 1))/log(2));
}

我想知道是否有其他方法可以实现相同的目标?有没有可能使用位操作来解决这个问题?

我这样做是为了迭代一个值并构建一些取决于迭代位置的操作,伪代码如下:

 while (temp) {
            auto cur_index = the_index(temp);
            auto window = ((1 << i) - 1) << cur_index;
            if (((temp & window) ^ window) == 0) {
              //....do something
            }
            temp &= temp - 1;
        }

【问题讨论】:

  • 该值可以设置多个位吗?在那种情况下,你想发生什么?
  • 听起来像std::countr_zero?
  • @HolyBlackCat:log 代码建议找到最高有效位集。那是std::bit_width
  • @MSalters val &amp; ((~val) + 1) 将除最低有效位 1 之外的所有位清零。
  • 直到 C++20,countr_zero 在 Boost.Core 中可用;现场演示:godbolt.org/z/hvdx9x6PE.

标签: c++ algorithm bit-manipulation bitwise-operators


【解决方案1】:

对此有一个标准函数:

auto cur_index = std::countr_zero(temp);

在我的系统上,编译为:

xor     eax, eax
tzcnt   eax, edi

请注意,无论输入是否恰好有一个设置位,此函数都会成功计算从右到第一个位的零位。

【讨论】:

  • 是的,来自 c++20。
【解决方案2】:

如果问题是关于在一个值中查找单个位的索引,比如uint32_t x = b000010000;,一种快速的方法是

auto index = builtin_popcount(x - 1);

// example: b000010000 - 1
//          b000001111
// popcount(b000001111) == 4

在 arm64 架构中,最好的方法是使用 count_leading_zero(x) 指令/操作,因为通用寄存器缺乏快速弹出计数。

更仔细地阅读 OP,不能保证 x 只有一个位集——问题似乎是找到最低有效位集的索引。可以用

来隔离
 x = temp & (0-temp);

【讨论】:

  • 在 arm64(没有 c++20)上,快速的方法是使用count_leading_zero(bit_reverse(temp)),其中temp 可以设置多个位。
  • 自动温度 = 0x08;自动 x = 温度 & (0-温度);这些代码得到的 x 等于 8。
  • 是的,有问题吗?给定任何非零变量temp,最低有效位由temp &amp; (-temp) 隔离。当temp 只包含一个位时,则x == temp。该位也可以稍后用于temp ^= x; 将其关闭。由于一元减号-uint32_t是UB或格式错误,处理它的正确方法是写成(0-temp);
  • 那可以得到最低有效位,但是如何得到它的索引呢?你想给我完整的代码吗,谢谢!
  • 第一段就讲到了,也就是auto index = builtin_popcount(x - 1);,还有intel内在函数,可以直接用int _bit_scan_reverse (temp);。如果temp == 0,输出将是未定义的。
【解决方案3】:

我建议您使用 __builtin_ctzl :返回 x 中尾随 0 位的数量,从最低有效位位置开始。 (警告:如果 x 为 0,则结​​果未定义)。

示例:__builtin_ctzl(0x10) = 4,__builtin_ctzl(0x20) = 5

对于您的情况,您可以像这样使用它:

unsigned index;
do {
    index = temp ? __builtin_ctzl(temp) : 0;
    ... //do your stuff
    temp -= pow(2, index);
} while (index);

【讨论】:

  • 我认为 OP 在使用temp &amp;= temp - 1 清除最低有效位方面已经做得很好了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-24
  • 1970-01-01
  • 2016-03-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多