简体   繁体   中英

Moving set bits to the end of 64bits integer

I am working on a function that takes a 64bits integer as parameter and returns a 64bits integer with all set bits at the end.

01011001 -> 00001111   // examples
00010100 -> 00000011

I first thought about the following algorithm:

nb_ones = countSetBit(x)
int64 res = 1
for i from 1 to nb_ones+1:
    res |= (1 << i)

Here countSetBit is the one defined here

Is there something more straightforward ? I am working in C++

countSetBit is probably something that's optimized for your platform already.

To set a given number of ones at the end, just go to the next power of two and subtract one.

nb_ones = countSetBit(x)
int64 res = nb_ones == 64 ? -1 : ((1 << nb_ones) - 1);

Edit: Nice non-branching solution from MSalters' comment:

int64_t res = ((1^(nb_ones>>6))<<nb_ones)-1;

(the 6th bit in nb_ones is one if-and-only-if nb_ones==64)

The background for the undefined behavior for << 64 is probably that the corresponding native operation might only use the bits of the argument needed for the maximum reasonable shift value, and handling this on the C++ side would add overhead.

You can avoid the loop:

const auto nb_ones = countSetBit(x)
if (nb_ones == 64) {
    return -1; // 0xFFFFFFFFFFFFFFFF;
} else {
    return (1u << nb_ones) - 1;
}

Counting all bits is a bit overkill as most CPU's have an efficient test against zero.

So, what we do is use that as the exit condition:

output = 0;
while (input != 0) {
  if (input & 1) output = (output<<1)+1;
  input >>= 1;
}

The loop shifts the input to the right, adding one extra bit to output whenever a bit is shifted out of input . Clearly this adds as many bits to output as there are in input (possibly 0, possibly 64). But the bits in output are contiguous as output is only shifted when a bit is added.

If your CPU has a bitcount operation, that's going to be faster of course. And if you'd implement this in x86 or ARM assembly, you'd use the fact that input&1 is the same bit that is shifted out by >>=1 .

Since you have several efficient answers, when you actually asked for a straightforward one, have a slow-but-conceptually-very-simple answer for variety:

uint64_t num(uint64_t x)
{
    // construct a base-2 string
    auto s = std::bitset<64>(x).to_string();
    // sort the 1s to the end
    std::sort(begin(s), end(s));
    // and convert it back to an integer
    return std::bitset<64>(s).to_ulong();
}

I think you can do it with just a single loop:

std::uint64_t bits_to_end(std::uint64_t n)
{
    std::uint64_t x = 0;

    for(std::uint64_t bit = 0, pos = 0; bit < 64; ++bit)
        if(n & (1ULL << bit))
            x |= (1ULL << pos++);

    return x;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM