最重要的设置位的未设置位数?

时间:2010-11-04 13:45:43

标签: c# c++ c 64-bit bit-manipulation

假设64位整数0x000000000000FFFF将表示为

00000000 00000000  00000000 00000000
00000000 00000000 >11111111 11111111

如何在最重要的设置位(标有>的那个)左侧找到未设置位的数量?

10 个答案:

答案 0 :(得分:6)

在直接C中(长整数在我的设置中是64位),取自类似的Java实现:(在更多关于汉明重量的读数后更新)

更多解释:顶部只是将所有位置于最重要的1的右侧,然后否定它。 (即最重要的1的'左'的0到现在都是1,其他的都是0)。

然后我使用Hamming Weight实现来计算位数。

unsigned long long i = 0x0000000000000000LLU;

i |= i >> 1;
i |= i >> 2;
i |= i >> 4;
i |= i >> 8;
i |= i >> 16;
i |= i >> 32;
// Highest bit in input and all lower bits are now set. Invert to set the bits to count.
i=~i;

i -= (i >> 1) & 0x5555555555555555LLU; // each 2 bits now contains a count
i = (i & 0x3333333333333333LLU) + ((i >> 2) & 0x3333333333333333LLU); // each 4 bits now contains a count
i = (i + (i >> 4)) & 0x0f0f0f0f0f0f0f0fLLU; // each 8 bits now contains a count 
i *= 0x0101010101010101LLU; // add each byte to all the bytes above it
i >>= 56; // the number of bits

printf("Leading 0's = %lld\n", i);

我很想知道这是多么有效率。虽然测试了几个值,但似乎有效。

答案 1 :(得分:4)

基于:http://www.hackersdelight.org/HDcode/nlz.c.txt

template<typename T> int clz(T v) {int n=sizeof(T)*8;int c=n;while (n){n>>=1;if (v>>n) c-=n,v>>=n;}return c-v;}

如果您想要一个允许您保持午餐的版本,请转到:

int clz(uint64_t v) {
    int n=64,c=64;
    while (n) {
        n>>=1;
        if (v>>n) c-=n,v>>=n;
    }
    return c-v;
}

正如您所看到的,您可以通过仔细分析汇编程序来节省周期,但这里的策略并不可怕。 while循环将运行Lg [64] = 6次;每次它将问题转换为计算一半大小的整数的前导位数。 while循环中的if语句提出了一个问题:“我可以将这个整数表示为这个整数的一半”,或类似地说,“如果我把它减半,我丢失了吗?”。 if()有效负载完成后,我们的数字将始终位于最低的n位。 在最后阶段,v为0或1,这样就可以正确完成计算。

答案 2 :(得分:2)

如果您正在处理无符号整数,则可以这样做:

#include <math.h>
int numunset(uint64_t number)
{
    int nbits = sizeof(uint64_t)*8;
    if(number == 0)
        return nbits;
    int first_set = floor(log2(number));
    return nbits - first_set - 1;
}

我不知道它将如何在性能上与已经提供的循环和计数方法进行比较,因为log2()可能很昂贵。

修改

这可能会导致高值整数出现一些问题,因为log2()函数正在转换为double,并且可能会出现一些数值问题。您可以使用与log2l()一起使用的long double函数。更好的解决方案是使用this question中的整数log2()函数。

答案 3 :(得分:1)

我不确定我是否正确理解了这个问题。我认为你有一个64位的值,并希望找到其中前导零的数量。

一种方法是找到最高有效位并简单地从63减去其位置(假设最低位为位0)。您可以通过测试是否在所有64位的循环内设置位来找出最重要的位。

另一种方法可能是在gcc中使用(非标准)__builtin_clz

答案 4 :(得分:1)

user470379's相同,但倒数......
假设未设置所有64位。当值大于0时,保持向右移动值并递减未设置位的数量:

/* untested */
int countunsetbits(uint64_t val) {
    int x = 64;
    while (val) { x--; val >>= 1; }
    return x;
}

答案 5 :(得分:1)

我同意二元搜索的想法。但是有两点很重要:

  1. 您问题的有效答案范围为0到64 包含。换句话说 - 问题可能有 65 不同的答案。我认为(几乎可以肯定)所有发布“二分搜索”解决方案的人都错过了这一点,因此在MSB位开启时,他们会得到零或数字的错误答案。
  2. 如果速度至关重要 - 您可能希望避免循环。使用模板可以实现这一目标。
  3. 以下模板内容可以正确找到任何无符号类型变量的MSB。

    // helper
    template <int bits, typename T>
    bool IsBitReached(T x)
    {
        const T cmp = T(1) << (bits ? (bits-1) : 0);
        return (x >= cmp);
    }
    
    template <int bits, typename T>
    int FindMsbInternal(T x)
    {
        if (!bits)
            return 0;
    
        int ret;
        if (IsBitReached<bits>(x))
        {
            ret = bits;
            x >>= bits;
        } else
            ret = 0;
    
        return ret + FindMsbInternal<bits/2, T>(x);
    }
    
    // Main routine
    template <typename T>
    int FindMsb(T x)
    {
        const int bits = sizeof(T) * 8;
        if (IsBitReached<bits>(x))
            return bits;
    
        return FindMsbInternal<bits/2>(x);
    }
    

答案 6 :(得分:1)

在这里,您可以轻松更新其他尺寸......

int bits_left(unsigned long long value)
{
  static unsigned long long mask = 0x8000000000000000;
  int c = 64;
  // doh
  if (value == 0)
    return c;

  // check byte by byte to see what has been set
  if (value & 0xFF00000000000000)
    c = 0;
  else if (value & 0x00FF000000000000)
    c = 8;
  else if (value & 0x0000FF0000000000)
    c = 16;
  else if (value & 0x000000FF00000000)
    c = 24;
  else if (value & 0x00000000FF000000)
    c = 32;
  else if (value & 0x0000000000FF0000)
    c = 40;
  else if (value & 0x000000000000FF00)
    c = 48;
  else if (value & 0x00000000000000FF)
    c = 56;

  // skip
  value <<= c;

  while(!(value & mask))
  {
    value <<= 1;
    c++;
  }

  return c;
}

答案 7 :(得分:1)

// clear all bits except the lowest set bit
x &= -x;     

// if x==0, add 0, otherwise add x - 1. 
// This sets all bits below the one set above to 1.
x+= (-(x==0))&(x - 1);

return 64 - count_bits_set(x);

count_bits_set是最快的计数位版本。有关各种位计数技术,请参阅https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel

答案 8 :(得分:0)

尝试

int countBits(int value)
{
    int result = sizeof(value) * CHAR_BITS;  // should be 64

    while(value != 0)
    {
        --result;
        value = value >> 1; // Remove bottom bits until all 1 are gone.
    }
    return result;
}

答案 9 :(得分:0)

使用log base 2获取最重要的数字1。

log(2) = 1, meaning 0b10 -> 1
log(4) = 2, 5-7 => 2.xx, or 0b100 -> 2
log(8) = 3, 9-15 => 3.xx, 0b1000 -> 3
log(16) = 4 you get the idea

依旧...... 中间的数字成为日志结果的分数。因此,将值转换为int可以为您提供最重要的数字。

一旦你得到这个数字,比如b,简单的64 - n将是答案。

function get_pos_msd(int n){
    return int(log2(n))
}

last_zero = 64 - get_pos_msd(n)