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

时间:2014-07-26 01:59:09

标签: c++ c c++11 bit-manipulation c99

考虑这个程序

#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或类似的文字,我找不到任何快速的小辫子。

This有点像它但它容易出错,它永远不会通过转换测试或一组关于转换的警告标志,至少在我的情况下如此。此外,它可能是一个非最佳选择。

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

4 个答案:

答案 0 :(得分:4)

如果这是X86并且你可以使用汇编,那么就是位扫描反向指令。根据编译器的不同,可能存在固有的内容。

bit scan reverse

答案 1 :(得分:2)

为什么您无法获得黑客的喜悦?代理限制?

以下是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;
}

答案 2 :(得分:2)

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

可以在此处找到如何为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的幂的一个,然后de Bruijn乘法和查找完成剩下的工作。当已知输入数是2的幂时(虽然幻数不同),不需要移位。所有信息均可用here

答案 3 :(得分:1)

如果您正在使用visual studio,则可以使用BitScanRevers内在函数。