Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/157.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 计算连续1位的最快方法。C++;_C++_Count_Bit Manipulation_Bit - Fatal编程技术网

C++ 计算连续1位的最快方法。C++;

C++ 计算连续1位的最快方法。C++;,c++,count,bit-manipulation,bit,C++,Count,Bit Manipulation,Bit,基本上,我只需要知道整数或无符号整数中最高1位的位置。例如: 00001111=4; 00011111=5; 11111111=8; 因为我确信我得到的任何数字都会有连续的1位。0…0000011…1将没有..00010011。。。或者别的什么。所以方法可以找到最高的1,或者只计算1s。没关系 这是我能做到的最好的事情: Uint32 number; int shift=16; int segment=8; while (segment) { if (number>>shift!

基本上,我只需要知道整数或无符号整数中最高1位的位置。例如:

00001111=4;
00011111=5;
11111111=8;
因为我确信我得到的任何数字都会有连续的1位。0…0000011…1将没有..00010011。。。或者别的什么。所以方法可以找到最高的1,或者只计算1s。没关系

这是我能做到的最好的事情:

Uint32 number;
int shift=16; int segment=8;
while (segment) 
{
if (number>>shift!=0) shift+=segment; 
else shift-=segment;
segment>>1; // /2
}
(一)

您可以计算需要对无符号int进行多少次位移位,直到它为零

(二)

范例

电话:0111

将一位右移:0011,使用原始编号0111^0011=0100的按位x-or

在cpp中:

unsigned int num = 3;

unsigned int answer = ((num >> 1) ^ (num)); 

cout << answer << '\n';
unsigned int num=3;
无符号整数回答=((num>>1)^(num));
库特
返回从0开始的最高值的位置,如果没有,则返回-1

getHighestOne(0)将返回-1
getHighestOne(1)将返回0
getHighestOne(10)将返回3

编辑: 下面是一些快速日志方法的示例。

int count\u位(unsigned int num){
int count_bit(unsigned int num) {
    // Assume 0<= num < 256, i.e. 8 bit
    // It can be easily support larger word size by successive calling.
    if ( num >= 256 ) return -1;
    return count[ num ];
}
//假设0=256)返回-1; 返回计数[num]; }

是的,最快的方法是查找表。

复制/粘贴我的函数:

size_t FirstSetBit(unsigned int v) const
{
#if defined(_MSC_VER)
    unsigned long ul;
    // Just 10% faster than MultiplyDeBruijnBitPosition method, on Core i7
    _BitScanForward(&ul, v);
    return ul;
#elif defined(__GNUC__) || defined(__clang__)
    return 31 - __builtin_clz(v);
#else // integer fallback for non-x64
    #warning You may be able to optimise this code for your compiler/processor

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

    r = MultiplyDeBruijnBitPosition[(uint32_t((v & -int(v)) * 0x077CB531U)) >> 27];
return r;
#endif
}

假设您知道所讨论的int的大小(例如,32位),您可以非常轻松地使用二进制搜索来搜索设置的最高位:

int bit_pos(unsigned value) { 
    static const std::vector<unsigned> masks = {
        0xffff0000, 
        0xff00ff00, 
        0xf0f0f0f0, 
        0xcccccccc, 
        0xaaaaaaaa
    };

    if (!value)
        return 0;

    int position = 0;

    int val = 16;

    for (unsigned mask : masks) {
        if (value & mask) {
            position += val;
            value &= mask;
        }
        val /= 2;
    }

    return position + 1;
}
对于64位整数,数字会变大,但我们只需再添加一次迭代:

unsigned bit_pos64(unsigned long long value) {
    value = ~value;
    unsigned position = 64;
    value &= -(long long)value;
    if (value) --position;
    if (value & 0x00000000ffffffff) position -= 32;
    if (value & 0x0000ffff0000ffff) position -= 16;
    if (value & 0x00ff00ff00ff00ff) position -= 8;
    if (value & 0x0f0f0f0f0f0f0f0f) position -= 4;
    if (value & 0x3333333333333333) position -= 2;
    if (value & 0x5555555555555555) position -= 1;
    return position;
}

通过仅设置一个位,我们避免了循环迭代之间的依赖关系,因此可以并行执行迭代。手动展开循环(如上所述)可能有助于降低发生这种情况的可能性,至少是轻微的。这也需要每次迭代只进行一次操作,而不是2次,因此即使没有任何并行执行,也可能更快。

其他人给出了各种各样的答案,但可能值得一提的是,有一整本书都包含了这类内容,这本书也有

还有一个值得强调的事实是,有些微处理器对这类事情有特殊的支持。拉塞·莱因霍尔德(Lasse Reinhold)在上文中的回答利用了这一事实,但没有引起读者的注意。一般来说,除了非常简单的情况(如位旋转指令)外,编译器无法将“位旋转”算法优化为单个机器指令,因此如果您知道您所在的机器上有位向前扫描或人口计数指令或类似指令,并且您可以使用它(通过编译器内部函数或
asm
语句),您可能需要这样做

最后,由于问题是从已知数字的形式0…000111…1开始的,我将添加另一个选项,基于计算(并行)位组的总和:

uint32_t count_set_bits(uint32_t x) {
  x = ((x >> 1) & 0x55555555) + (x & 0x55555555);
  x = ((x >> 2) & 0x33333333) + (x & 0x33333333);
  x = ((x >> 4) & 0x0f0f0f0f) + (x & 0x0f0f0f0f);
  x = ((x >> 8) + x) & 0x00ff00ff);
  return (x >> 16) + x) & 0x0000ffff;
}

你所做的被称为a。在你的例子中,它被称为位扫描反转。它也是对数基二算法的基础

x86指令集有一条用于此的指令,
bsr
=位扫描反转,自Intel 386以来。因此,您应该尝试使用一个函数,在可能的情况下调用该指令。对于MSVC,您希望使用
\u位扫描反转
、GCC
31-\u内置clz(x)
、ICC
\u位扫描反转

我查看了来自MSVC和GCC的这些内部函数/内置函数的汇编输出,它们都生成了
bsr
指令

英特尔Haswell处理器添加了
lzcnt
指令(AMD在巴塞罗那很早就添加了该指令)。这将计算前导零。它与31-bsr相同(或等于bsr-请参见下面的警告)。您可以使用MSVC使用_lzcnt调用它。但是,如果您在不支持lzcnt的处理器上执行此操作,则应警告您

lzcnt的编码与bsr非常相似,如果在不支持lzcnt的CPU(如Haswell之前的英特尔CPU)上执行lzcnt,则它将执行bsr操作,而不会引发无效指令错误

如果要在软件中执行BitScanReverse,有几种不同的方法。请参阅“查找N位整数的对数基数2”一节
下面的代码可能有助于计算连续的1位

int count_consecutive_ones(int in) {
    int count = 0;
    while (in) {
        in = (in & (in << 1));
        count++;
    }
    return count;
}
int计数\u连续的\u计数(int-in){
整数计数=0;
当{

in=(in&)但是OP要求的是“最快”的方法,而不仅仅是“任何”。不?谢谢你的评论,我添加了第二种方法。我认为这样更好一些……标题是“连续”但您的示例仅查找最高位。您能详细说明吗?更正了问题。英特尔CPU包含了一条执行此操作的指令。搜索黑客对答案的兴趣,但这是“最快”的吗方法?我不知道,但它应该足够快?我希望有更快的方法,比如最多5个操作。@Quit Canner:现在它只有4个操作长。有关更多方法和解释,请参阅我编辑中的链接。基于循环的解决方案不是最好的方法。请参阅下面的其他答案。如果且仅当整个表已经在缓存中。在现代处理器上,CPU操作比内存读取快得多。如果操作导致大量依赖项和其他暂停,则CPU操作不需要更快。在这种情况下,有很多因素,并且在测量特定的使用模式之前,没有绝对最快的方法。无论如何,如果就性能而言,我假设此操作调用了很多次,并且如此小的表应该保留在缓存中。所涉及的CPU操作不需要太多依赖项,因此暂停的可能性非常小。即使对于64位整数,最多也需要调用8次,然后将中间结果相加——这已经是更多的操作了han(举一个明显的例子)我的答案中的代码。
clz
意味着计算前导零,这样就不会返回第一个位集。您需要将解决方案修改为类似32 clz的值。
uint32_t count_set_bits(uint32_t x) {
  x = ((x >> 1) & 0x55555555) + (x & 0x55555555);
  x = ((x >> 2) & 0x33333333) + (x & 0x33333333);
  x = ((x >> 4) & 0x0f0f0f0f) + (x & 0x0f0f0f0f);
  x = ((x >> 8) + x) & 0x00ff00ff);
  return (x >> 16) + x) & 0x0000ffff;
}
int count_consecutive_ones(int in) {
    int count = 0;
    while (in) {
        in = (in & (in << 1));
        count++;
    }
    return count;
}