Number of unset bit left of most significant set b

2020-07-10 09:08发布

Assuming the 64bit integer 0x000000000000FFFF which would be represented as

00000000 00000000  00000000 00000000
00000000 00000000 >11111111 11111111

How do I find the amount of unset bits to the left of the most significant set bit (the one marked with >) ?

10条回答
萌系小妹纸
2楼-- · 2020-07-10 09:51

Based on: 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;}

If you'd like a version that allows you to keep your lunch down, here you go:

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

As you'll see, you can save cycles on this by careful analysis of the assembler, but the strategy here is not a terrible one. The while loop will operate Lg[64]=6 times; each time it will convert the problem into one of counting the number of leading bits on an integer of half the size. The if statement inside the while loop asks the question: "can i represent this integer in half as many bits", or analogously, "if i cut this in half, have i lost it?". After the if() payload completes, our number will always be in the lowest n bits. At the final stage, v is either 0 or 1, and this completes the calculation correctly.

查看更多
叛逆
3楼-- · 2020-07-10 09:54

If you are dealing with unsigned integers, you could do this:

#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;
}

I don't know how it will compare in performance to the loop and count methods that have already been offered because log2() could be expensive.

Edit:

This could cause some problems with high-valued integers since the log2() function is casting to double and some numerical issues may arise. You could use the log2l() function that works with long double. A better solution would be to use an integer log2() function as in this question.

查看更多
Ridiculous、
4楼-- · 2020-07-10 09:58
// 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);

Where count_bits_set is the fastest version of counting bits you can find. See https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel for various bit counting techniques.

查看更多
戒情不戒烟
5楼-- · 2020-07-10 09:59

I'm not sure I understood the problem correctly. I think you have a 64bit value and want to find the number of leading zeros in it.

One way would be to find the most significant bit and simply subtract its position from 63 (assuming lowest bit is bit 0). You can find out the most significant bit by testing whether a bit is set from within a loop over all 64 bits.

Another way might be to use the (non-standard) __builtin_clz in gcc.

查看更多
戒情不戒烟
6楼-- · 2020-07-10 10:00

I agree with the binary search idea. However two points are important here:

  1. The range of valid answers to your question is from 0 to 64 inclusive. In other words - there may be 65 different answers to the question. I think (almost sure) all who posted the "binary search" solution missed this point, hence they'll get wrong answer for either zero or a number with the MSB bit on.
  2. If speed is critical - you may want to avoid the loop. There's an elegant way to achieve this using templates.

The following template stuff finds the MSB correctly of any unsigned type variable.

// 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);
}
查看更多
做个烂人
7楼-- · 2020-07-10 10:00

Same idea as user470379's, but counting down ...
Assume all 64 bits are unset. While value is larger than 0 keep shifting the value right and decrementing number of unset bits:

/* untested */
int countunsetbits(uint64_t val) {
    int x = 64;
    while (val) { x--; val >>= 1; }
    return x;
}
查看更多
登录 后发表回答