【问题标题】:Moving set bits to the end of 64bits integer将设置位移动到 64 位整数的末尾
【发布时间】:2017-11-09 14:53:24
【问题描述】:

我正在开发一个函数,该函数将 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++

【问题讨论】:

标签: c++ algorithm bit-manipulation


【解决方案1】:

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

要在末尾设置给定数量的 1,只需乘以 2 的下一个幂并减去 1。

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

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

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

(nb_ones 中的第 6 位是一个 if-and-only-if nb_ones==64)

【讨论】:

  • x == (int64)-1 的情况如何?因为1 &lt;&lt; 64 会触发未定义的行为
  • 是的,就像那样(有一个赞成票),但你确实需要检查边缘情况-1
  • 谢谢,已修复 O:)
  • 有一个非分支解决方案:int64_t res = ((1^(nb_ones&gt;&gt;6))&lt;&lt;nb_ones)-1; - nb_ones 中的第 6 位是一个 if-and-only-if nb_ones==64
【解决方案2】:

你可以避免循环:

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

【讨论】:

  • ͏+͏1͏。出于兴趣,你真的觉得0xFFFFFFFFFFFFFFFF-1 更清晰吗?还是我是一只奇怪的猫?
【解决方案3】:

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

所以,我们所做的就是使用它作为退出条件:

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

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

如果您的 CPU 具有位计数操作,那当然会更快。如果您要在 x86 或 ARM 程序集中实现这一点,您将使用 input&amp;1&gt;&gt;=1 移出的同一位这一事实。

【讨论】:

  • 你迟到了,但这是最好的答案。投赞成票!
【解决方案4】:

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

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

【讨论】:

  • 这写得非常好,它向我展示了我实现了完全错误的东西。也有一个赞成票。
  • 我仍然不知道 为什么 OP 想要这些位,或者它可能有用的地方 - 这可能就是为什么我花了几篇阅读也要弄清楚。
【解决方案5】:

我认为你只需一个循环就可以做到:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-23
    • 2018-03-08
    • 1970-01-01
    • 1970-01-01
    • 2010-12-18
    相关资源
    最近更新 更多