我正在研究一个函数,该函数以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 ++工作
答案 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;
}