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

时间:2017-11-09 14:53:25

标签: c++ algorithm bit-manipulation

我正在研究一个函数,该函数以64位整数作为参数,并返回一个64位整数,最后包含所有设置位。

01011001 -> 00001111   // examples
00010100 -> 00000011

我首先想到了以下算法:

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

此处countSetBit是一个defined here

有更简单的事情吗?我在C ++工作

5 个答案:

答案 0 :(得分:4)

countSetBit可能已经针对您的平台进行了优化。

要在最后设置给定数量的1,只需转到下一个2的幂并减去1。

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

编辑:来自MSalters评论的优秀非分支解决方案:

{{1}}

(nb_ones中的第6位是if-and-only-if,如果nb_ones == 64)

&lt;&lt;的未定义行为的背景64可能是相应的本机操作可能只使用最大合理移位值所需的参数位,并且在C ++端处理它会增加开销。

答案 1 :(得分:3)

你可以避免循环:

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

答案 2 :(得分:3)

计算所有位有点过分,因为大多数CPU都有针对零的有效测试。

所以,我们所做的是将其用作退出条件:

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

循环将输入向右移动,只要有一个位移出output,就会向input添加一个额外位。显然,这会增加与output中的input一样多的位(可能为0,可能为64)。但output中的位是连续的,因为output仅在添加位时才会移位。

如果你的CPU有一个bitcount操作,那当然会更快。如果您在x86或ARM程序集中实现此功能,则可以使用input&1>>=1移出的位相同的事实。

答案 3 :(得分:2)

由于您有几个高效的答案,当您实际上要求简单时,有一个缓慢但概念上非常简单的答案:

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();
}

答案 4 :(得分:0)

我认为你只需一个循环即可完成:

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