简体   繁体   English

将设置位移动到64位整数的末尾

[英]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. 我正在开发一个函数,它接受一个64位整数作为参数,并返回一个64位整数,最后包含所有设置位。

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 这里countSetBit这里定义的

Is there something more straightforward ? 有更直接的事情吗? I am working in C++ 我在C ++工作

countSetBit is probably something that's optimized for your platform already. countSetBit可能已经针对您的平台进行了优化。

To set a given number of ones at the end, just go to the next power of two and subtract one. 要在最后设置给定数量的1,只需转到下一个2的幂并减去1。

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

Edit: Nice non-branching solution from MSalters' comment: 编辑:来自MSalters评论的不错的非分支解决方案:

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) (如果nb_ones == 64,则nb_ones中的第6位是if-and-only-only)

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. << 64的未定义行为的背景可能是相应的本机操作可能只使用最大合理移位值所需的参数位,并且在C ++端处理此操作会增加开销。

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. 计算所有位有点过分,因为大多数CPU都有针对零的有效测试。

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 . 循环将输入向右移动,每当一个位移出input时,就会output一个额外的位。 Clearly this adds as many bits to output as there are in input (possibly 0, possibly 64). 显然,这会增加与input output位数(可能为0,可能为64)。 But the bits in output are contiguous as output is only shifted when a bit is added. output中的位是连续的,因为只有在添加位时才会移位output

If your CPU has a bitcount operation, that's going to be faster of course. 如果你的CPU有一个bitcount操作,那当然会更快。 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 . 如果你在x86或ARM程序集中实现它,你会使用input&1是由>>=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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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