How to find the position of the only-set-bit in a 64-bit value using bit manipulation efficiently?

后端 未结 9 1713
有刺的猬
有刺的猬 2020-12-24 05:51

Just say I have a value of type uint64_t seen as sequence of octets (1 octet = 8-bit). The uint64_t value is known containing only one set bit<

9条回答
  •  野趣味
    野趣味 (楼主)
    2020-12-24 06:12

    C++ tag was removed, but here is a portable C++ answer nonetheless since you can compile it with C++ and use an extern C interface:

    If you have a power of 2 and you subtract one you end up with a binary number with the number of set bits equal to the position

    A way to count the number of set bits (binary 1s) is wrapped, presumably most efficiently by each implementation of the stl, in std::bitset member function count

    Note that your specification has 0 returned for both 0 or 1, so I added as_specified_pos to meet this requirement. Personally I would just leave it return the natural value of 64 when passed 0 to be able to differentiate, and for the speed.

    The following code should be extremely portable and most likely optimized per platform by compiler vendors:

    #include 
    
    uint64_t pos(uint64_t val)
    {
       return std::bitset<64>(val-1).count();
    }
    
    uint64_t as_specified_pos(uint64_t val)
    {
        return (val) ? pos(val) : 0;
    }
    

    On Linux with g++ I get the following disassembled code:

    0000000000000000 :
       0:   48 8d 47 ff             lea    -0x1(%rdi),%rax
       4:   f3 48 0f b8 c0          popcnt %rax,%rax
       9:   c3                      retq
       a:   66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)
    
    0000000000000010 :
      10:   31 c0                   xor    %eax,%eax
      12:   48 85 ff                test   %rdi,%rdi
      15:   74 09                   je     20 
      17:   48 8d 47 ff             lea    -0x1(%rdi),%rax
      1b:   f3 48 0f b8 c0          popcnt %rax,%rax
      20:   f3 c3                   repz retq
    

提交回复
热议问题