Position of least significant bit that is set

后端 未结 23 955
时光取名叫无心
时光取名叫无心 2020-11-22 08:46

I am looking for an efficient way to determine the position of the least significant bit that is set in an integer, e.g. for 0x0FF0 it would be 4.

A trivial impleme

相关标签:
23条回答
  • 2020-11-22 09:13

    Bit Twiddling Hacks offers an excellent collection of, er, bit twiddling hacks, with performance/optimisation discussion attached. My favourite solution for your problem (from that site) is «multiply and lookup»:

    unsigned int v;  // find the number of trailing zeros in 32-bit v 
    int r;           // result goes here
    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 & -v) * 0x077CB531U)) >> 27];
    

    Helpful references:

    • "Using de Bruijn Sequences to Index a 1 in a Computer Word" - Explanation about why the above code works.
    • "Board Representation > Bitboards > BitScan" - Detailed analysis of this problem, with a particular focus on chess programming
    0 讨论(0)
  • 2020-11-22 09:14

    OMG has this just spiraled.

    What most of these examples are lacking is a little understanding about how all hardware works.

    Anytime you have a branch, the CPU has to guess which branch will be taken. The instruction pipe is loaded with the instructions that lead down the guessed path. If the CPU has guessed wrong then the instruction pipe gets flushed, and the other branch must be loaded.

    Consider the simple while loop at the top. The guess will be to stay within the loop. It will be wrong at least once when it leaves the loop. This WILL flush the instruction pipe. This behavior is slightly better than guessing that it will leave the loop, in which case it would flush the instruction pipe on every iteration.

    The amount of CPU cycles that are lost varies highly from one type of processor to the next. But you can expect between 20 and 150 lost CPU cycles.

    The next worse group is where you think your going to save a few iterations by splitting the value in to smaller pieces and adding several more branches. Each of these branches adds an additional opportunity to flush the instruction pipe and cost another 20 to 150 clock cycles.

    Lets consider what happens when you look up a value in a table. Chances are the value is not currently in cache, at least not the first time your function is called. This means that the CPU gets stalled while the value is loaded from cache. Again this varies from one machine to the next. The new Intel chips actually use this as an opportunity to swap threads while the current thread is waiting for the cache load to complete. This could easily be more expensive than an instruction pipe flush, however if you are performing this operation a number of times it is likely to only occur once.

    Clearly the fastest constant time solution is one which involves deterministic math. A pure and elegant solution.

    My apologies if this was already covered.

    Every compiler I use, except XCODE AFAIK, has compiler intrinsics for both the forward bitscan and the reverse bitscan. These will compile to a single assembly instruction on most hardware with no Cache Miss, no Branch Miss-Prediction and No other programmer generated stumbling blocks.

    For Microsoft compilers use _BitScanForward & _BitScanReverse.
    For GCC use __builtin_ffs, __builtin_clz, __builtin_ctz.

    Additionally, please refrain from posting an answer and potentially misleading newcomers if you are not adequately knowledgeable about the subject being discussed.

    Sorry I totally forgot to provide a solution.. This is the code I use on the IPAD which has no assembly level instruction for the task:

    unsigned BitScanLow_BranchFree(unsigned value)
    {
        bool bwl = (value & 0x0000ffff) == 0;
        unsigned I1 = (bwl * 15);
        value = (value >> I1) & 0x0000ffff;
    
        bool bbl = (value & 0x00ff00ff) == 0;
        unsigned I2 = (bbl * 7);
        value = (value >> I2) & 0x00ff00ff;
    
        bool bnl = (value & 0x0f0f0f0f) == 0;
        unsigned I3 = (bnl * 3);
        value = (value >> I3) & 0x0f0f0f0f;
    
        bool bsl = (value & 0x33333333) == 0;
        unsigned I4 = (bsl * 1);
        value = (value >> I4) & 0x33333333;
    
        unsigned result = value + I1 + I2 + I3 + I4 - 1;
    
        return result;
    }
    

    The thing to understand here is that it is not the compare that is expensive, but the branch that occurs after the compare. The comparison in this case is forced to a value of 0 or 1 with the .. == 0, and the result is used to combine the math that would have occurred on either side of the branch.

    Edit:

    The code above is totally broken. This code works and is still branch-free (if optimized):

    int BitScanLow_BranchFree(ui value)
    {
        int i16 = !(value & 0xffff) << 4;
        value >>= i16;
    
        int i8 = !(value & 0xff) << 3;
        value >>= i8;
    
        int i4 = !(value & 0xf) << 2;
        value >>= i4;
    
        int i2 = !(value & 0x3) << 1;
        value >>= i2;
    
        int i1 = !(value & 0x1);
    
        int i0 = (value >> i1) & 1? 0 : -32;
    
        return i16 + i8 + i4 + i2 + i1 + i0;
    }
    

    This returns -1 if given 0. If you don't care about 0 or are happy to get 31 for 0, remove the i0 calculation, saving a chunk of time.

    0 讨论(0)
  • 2020-11-22 09:19

    recently I see that singapore's premier posted a program he wrote on facebook, there is one line to mention it..

    The logic is simply "value & -value", suppose you have 0x0FF0, then, 0FF0 & (F00F+1) , which equals 0x0010, that means the lowest 1 is in the 4th bit.. :)

    0 讨论(0)
  • 2020-11-22 09:21

    If C++11 is available for you, a compiler sometimes can do the task for you :)

    constexpr std::uint64_t lssb(const std::uint64_t value)
    {
        return !value ? 0 : (value % 2 ? 1 : lssb(value >> 1) + 1);
    }
    

    Result is 1-based index.

    0 讨论(0)
  • 2020-11-22 09:22

    Why not use the built-in ffs? (I grabbed a man page from Linux, but it's more widely available than that.)

    ffs(3) - Linux man page

    Name

    ffs - find first bit set in a word

    Synopsis

    #include <strings.h>
    int ffs(int i);
    #define _GNU_SOURCE
    #include <string.h>
    int ffsl(long int i);
    int ffsll(long long int i);
    

    Description

    The ffs() function returns the position of the first (least significant) bit set in the word i. The least significant bit is position 1 and the most significant position e.g. 32 or 64. The functions ffsll() and ffsl() do the same but take arguments of possibly different size.

    Return Value

    These functions return the position of the first bit set, or 0 if no bits are set in i.

    Conforming to

    4.3BSD, POSIX.1-2001.

    Notes

    BSD systems have a prototype in <string.h>.

    0 讨论(0)
提交回复
热议问题