获取整数bitset中前导1位置的最快方法

Quickest way to get the position of the leading 1 in a bitset for an integer?

本文关键字:位置 方法 整数 bitset 获取      更新时间:2023-10-16

考虑这个程序

#include <iostream>
#include <bitset>
#include <cstdint>
#include <cstdlib>
typedef uint8_t Tnum;
template <typename T>
void printBits(T a)
{
    std::cout << std::bitset<(sizeof(a) * 8)>(a).to_string() << 'n';
}
int main()
{
    printBits(Tnum(15));
    printBits(Tnum(17));
    return EXIT_SUCCESS;
}

它打印

00001111
00010001

现在考虑前面输出

中的这两个家伙
00001111
    ^
00010001
   ^

我想知道如何,给定有符号或无符号整数类型,并给定该类型实例的值,我可以获得模式中领先1的位置,从0开始计数,我期望的结果是3为第一行,4为第二行。所涉及的位置总数对我来说也是可以接受的,比如第一行用4,第二行用5

我现在没有Hacker's Delight或类似的文本可用,我找不到任何快速的旋转。

这有点像它,但它很容易出错,它永远不会通过转换测试或一组关于转换的警告标志,至少在我的情况下。而且这可能不是最优选择。

请不要查找表,我愿意接受任何不会导致转换问题且不使用LUT的东西。C89/99和c++ 11

如果这是X86并且您可以使用汇编,则有位扫描反向指令。根据编译器的不同,这可能有一个内在的

位扫描反转

为什么你不能进入黑客的喜悦?代理限制?

这是来自http://www.hackersdelight.org/hdcodetxt/nlz.c.txt

的解决方案
int nlz1(unsigned x) {
   int n;
   if (x == 0) return(32);
   n = 0;
   if (x <= 0x0000FFFF) {n = n +16; x = x <<16;}
   if (x <= 0x00FFFFFF) {n = n + 8; x = x << 8;}
   if (x <= 0x0FFFFFFF) {n = n + 4; x = x << 4;}
   if (x <= 0x3FFFFFFF) {n = n + 2; x = x << 2;}
   if (x <= 0x7FFFFFFF) {n = n + 1;}
   return n;
}

正如其他人所说,x86-64处理器具有最高有效位(MSB)指令,可以通过编译器使用内联汇编或编译器指令(intrinsic )访问该指令。Microsoft C编译器有_BitScanReverse 32位指令。

如何为gcc编译器插入内联汇编代码的示例可以在这里找到:https://www.biicode.com/pablodev/pablodev/bitscan/master/25/bitboard.h

如果您对这种类型的解决方案不感兴趣,那么使用de Bruijn幻数在表大小和速度之间取得良好折衷的O(log(N))解决方案是:

uint32_t v;
int r;
static const int MultiplyDeBruijnBitPosition[32] = 
{
  0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30,
  8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31
};
v |= v >> 1;  
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
r = MultiplyDeBruijnBitPosition[(uint32_t)(v * 0x07C4ACDDU) >> 27];

基本上,首先将输入的数字四舍五入到小于2次幂的1,然后进行de Bruijn乘法和查找。当已知输入数是2的幂时(但幻数是不同的),移位是不必要的。

如果你正在使用visual studio,你可以使用BitScanRevers intrinsic