【问题标题】:Getting the index of the leftmost active bit in an integer instantly立即获取整数中最左边的活动位的索引
【发布时间】:2018-05-23 08:20:27
【问题描述】:

如何从左到右而不是从右到左扫描整数(二进制)?我知道我可以从左边开始尝试每一位,然后记录最左边的位,但是有更快的方法吗?是否有内置函数可以立即找到整数中最左边的活动位(即 1)?

我知道从右到左,我可以做类似的事情

int myInt = 1234;
for(int i = 0; i < 32; i++) {
  int curr_bit = myInt & (1 << i);
  // do something with curr_bit
}

但是,我想从最左边的可用位开始,我想要它的数字“x”,以便1 &lt;&lt; x 指向那个确切的数字 (顺便说一句,我正在尝试实现重复平方,我的代码中需要它)。

任何帮助将不胜感激!

【问题讨论】:

  • @NickyC 这不是我的问题...我希望能够立即获得最左边的索引...对不起,如果我的问题有点不清楚。如果我右移,那么如果二进制中的整数类似于 1010,那么我必须从最左边的 2^32 位右移 28 次,然后才能得到第一个 1
  • 好的,现在我明白你的问题了。你听说过对数吗?
  • 试试吧。 int leftmost_bit_index = log2(myInt);
  • 你已经测量并与其他人进行了比较?

标签: c++ bit-manipulation bit built-in findfirst


【解决方案1】:

使用这个:

int left_most_bit = myInt & (1 << (sizeof myInt * CHAR_BIT - 1));

它是如何工作的?

  • sizeof myInt 返回变量 myInt 的大小,以 字节 为单位
  • CHAR_BIT 是一个(可能)依赖于平台的宏,它告诉您一个字节中有多少位,通常为 8
  • 左移 1 得到最左边的位。

在 O(1) 时间内工作得很好,因为 sizeof myIntCHAR_BIT 都是编译时常量,所以整个表达式 (1 &lt;&lt; (sizeof myInt * CHAR_BIT - 1)) 也是编译时常量。然后编译器可以对其应用最大优化。

【讨论】:

  • 这很有帮助,非常感谢。你们能解释一下对这个答案的反对意见吗?我觉得挺好的……
  • 出现了一个小错误。我在大小计算结束时错过了-1
  • @NL628 投票赞成并接受有帮助的答案怎么样?
  • 这个答案给出了单词中最高位的当前值,但我认为提问者想要设置的第一位的索引——这是另一回事。
  • 这不能回答问题。它给出了最左边位的值;问题是如何找到值为 1 的最左边的位。
【解决方案2】:

iBug 的回答很有趣,我从来没有想过这样做。如果您正在进行大量计算,并且想要多次找到最左边的数字,我会在 c++11 中推荐 __builtin_clz。如果你执行 sn -p

for(int i = 31 - __builtin_clz; i >= 0; i--) {
    int left_most_bit = myInt & (1 << i);
}

这将从 left_most_bit 开始并向右移动。希望这可以帮助! Implementation of __builtin_clz

【讨论】:

  • 这是一个很好的答案,但sizeof 的开销绝不会比 for 循环更多。
  • @JustinRandall 哦,对不起,我的回答只是循环遍历数字。此循环的一次重复将获得第一个数字,这将循环其余部分。 OP 在他的问题陈述中说她正在学习重复平方,所以我只是发布了我通常用于该算法的代码
  • 哦,这很有帮助!是的,我试图实现重复平方,这完全符合我的代码,非常感谢!我希望我可以多次投票:O
  • 我认为 __builtin_clz 是 GCC 扩展,而不是 C++11 功能(这不是坏事,因为我也建议扩展;只是认为了解这一点很重要)。
  • @bobjoe628 你知道你可以只使用单数 they 而不是在句子中途改变 OP 的性别,对吧?
【解决方案3】:

如果您对实际最快的答案感兴趣(至少在桌面上),这里是:使用 Intel Compiler 和 Clang(可能还有 Visual Studio 和 GCC)支持的 _bit_scan_reverse 内在函数。

#include "immintrin.h"
int main() { printf("%i", _bit_scan_reverse(9)); }

结果:3(因为1&lt;&lt;3 = 89中设置的最高位)。

Documentation

如果您担心可移植性(因为您应该使用像这样的所有专有扩展),只需包含一个后备功能并使用预处理器来选择您需要的实现:

#ifdef __SSE__ // All SSE processors support bsf/bsr
#include "immintrin.h"
static inline int bit_scan_reverse(int n) { return _bit_scan_reverse(n); }
#else
// Fallback implementation here
#endif

请注意,_bit_scan_reverse 会为 n=0 返回一个未指定的值。如果这是一个问题,您可以在bit_scan_reverse 中的代码中添加一个三元组:return n == 0 ? 0 : _bit_scan_reverse(n);

【讨论】:

  • 代码块是否支持?
  • Codeblocks 是 IDE,而不是编译器。您的支持取决于您在 IDE 中使用的编译器。
  • 哦,很抱歉,我对这个真的很陌生,正如你所看到的,我的问题和 cmets 都很愚蠢:P
【解决方案4】:

Java 在 Integer 类中有一个方法称为highestOneBit(int value),它返回一个int 值,最多只有一位,位于指定int 中设置的最高有效(最左边)位的位置价值。它是这样实现的:

int highestOneBit(int value)
{
    value |= (value >>  1);
    value |= (value >>  2);
    value |= (value >>  4);
    value |= (value >>  8);
    value |= (value >> 16);
    return value - (value >> 1);
}

【讨论】:

    【解决方案5】:

    这称为Find first set,大多数现代架构都有一条指令可以快速完成此操作。在 C++20 中,它可以在 &lt;bit&gt; header 中使用 std::countl_zero 来完成

    int left_most_bit_pos = sizeof(myInt)*CHAR_BIT - std::countl_zero(myInt);
    int left_most_bit = myInt & (1 << left_most_bit_pos)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-03-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多