假设64位整数
0x000000000000FFFF
,其表示为:00000000 00000000 00000000 00000000
00000000 00000000 >11111111 11111111
我该如何找到最高位的设置位(用 > 标记)左侧未设置位的数量?
0x000000000000FFFF
,其表示为:00000000 00000000 00000000 00000000
00000000 00000000 >11111111 11111111
我该如何找到最高位的设置位(用 > 标记)左侧未设置位的数量?
unsigned long long i = 0x0000000000000000LLU;
i |= i >> 1;
i |= i >> 2;
i |= i >> 4;
i |= i >> 8;
i |= i >> 16;
i |= i >> 32;
// Highest bit in input and all lower bits are now set. Invert to set the bits to count.
i=~i;
i -= (i >> 1) & 0x5555555555555555LLU; // each 2 bits now contains a count
i = (i & 0x3333333333333333LLU) + ((i >> 2) & 0x3333333333333333LLU); // each 4 bits now contains a count
i = (i + (i >> 4)) & 0x0f0f0f0f0f0f0f0fLLU; // each 8 bits now contains a count
i *= 0x0101010101010101LLU; // add each byte to all the bytes above it
i >>= 56; // the number of bits
printf("Leading 0's = %lld\n", i);
我很想看看这个在效率方面的表现如何。虽然我已经用几个值进行了测试,但它似乎可以正常工作。
template<typename T> int clz(T v) {int n=sizeof(T)*8;int c=n;while (n){n>>=1;if (v>>n) c-=n,v>>=n;}return c-v;}
int clz(uint64_t v) {
int n=64,c=64;
while (n) {
n>>=1;
if (v>>n) c-=n,v>>=n;
}
return c-v;
}
#include <math.h>
int numunset(uint64_t number)
{
int nbits = sizeof(uint64_t)*8;
if(number == 0)
return nbits;
int first_set = floor(log2(number));
return nbits - first_set - 1;
}
log2
真的很耗费资源!我甚至都忘记了这种可能性。我不知道处理器FPU中这样的函数是如何实现的,但通常计算任何非算术函数都需要计算一些级数和。我认为这种事情需要大量的CPU周期。 - valdo和user470379的想法一样,但是倒计时...
假设所有64位都未设置。当值大于0时,继续向右移动该值并递减未设置位数:
/* untested */
int countunsetbits(uint64_t val) {
int x = 64;
while (val) { x--; val >>= 1; }
return x;
}
我同意二分查找的想法。然而,这里有两个重要的点:
以下模板内容可以正确地找到任何无符号类型变量的MSB。
// helper
template <int bits, typename T>
bool IsBitReached(T x)
{
const T cmp = T(1) << (bits ? (bits-1) : 0);
return (x >= cmp);
}
template <int bits, typename T>
int FindMsbInternal(T x)
{
if (!bits)
return 0;
int ret;
if (IsBitReached<bits>(x))
{
ret = bits;
x >>= bits;
} else
ret = 0;
return ret + FindMsbInternal<bits/2, T>(x);
}
// Main routine
template <typename T>
int FindMsb(T x)
{
const int bits = sizeof(T) * 8;
if (IsBitReached<bits>(x))
return bits;
return FindMsbInternal<bits/2>(x);
}
这里是代码,如果你需要适应其他尺寸,更新起来非常简单...
int bits_left(unsigned long long value)
{
static unsigned long long mask = 0x8000000000000000;
int c = 64;
// doh
if (value == 0)
return c;
// check byte by byte to see what has been set
if (value & 0xFF00000000000000)
c = 0;
else if (value & 0x00FF000000000000)
c = 8;
else if (value & 0x0000FF0000000000)
c = 16;
else if (value & 0x000000FF00000000)
c = 24;
else if (value & 0x00000000FF000000)
c = 32;
else if (value & 0x0000000000FF0000)
c = 40;
else if (value & 0x000000000000FF00)
c = 48;
else if (value & 0x00000000000000FF)
c = 56;
// skip
value <<= c;
while(!(value & mask))
{
value <<= 1;
c++;
}
return c;
}
// clear all bits except the lowest set bit
x &= -x;
// if x==0, add 0, otherwise add x - 1.
// This sets all bits below the one set above to 1.
x+= (-(x==0))&(x - 1);
return 64 - count_bits_set(x);
count_bits_set
是你能找到的最快版本的计算位数的方法。请访问 https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel 了解各种位计数技术。
if (x) x ^= x-1
实现...但只要进行了测试,就可以做 if (!x) return ...
,然后 0 可以映射到任何值。(最好是对于 0 让该函数未定义,并让调用者处理它。) - Jim Balter我不确定我正确理解了问题。我认为您有一个64位值,并想找到其中前导零的数量。
一种方法是找到最高有效位,然后将其位置从63中简单地减去(假设最低位是位0)。您可以通过在所有64位上循环时测试是否设置了位来找出最高有效位。
另一种方法可能是使用gcc中的(非标准)__builtin_clz
。
尝试
int countBits(int value)
{
int result = sizeof(value) * CHAR_BITS; // should be 64
while(value != 0)
{
--result;
value = value >> 1; // Remove bottom bits until all 1 are gone.
}
return result;
}
使用以2为底的对数,可以得到最高位数字为1。
log(2) = 1, meaning 0b10 -> 1
log(4) = 2, 5-7 => 2.xx, or 0b100 -> 2
log(8) = 3, 9-15 => 3.xx, 0b1000 -> 3
log(16) = 4 you get the idea
等等……
在这些数字之间,它们变成了对数结果的分数。因此,将该值强制转换为int类型会给出最重要的数字。
一旦您得到这个数字,比如b,简单的64-n就是答案。
function get_pos_msd(int n){
return int(log2(n))
}
last_zero = 64 - get_pos_msd(n)