C++ 2 的最大幂 <= n

C++ largest power of 2 <= n

本文关键字:lt C++      更新时间:2023-10-16

如何计算二的最大幂<=一些数字n?

现在我使用的是floor(log(n)/log(2)+0.05),但它似乎对所有n都不可靠。有什么更干净/不受舍入误差影响的吗?

我假设这个数字是无符号的。您可以使用二进制表示的知识。

该数字将是在相同MSB位置具有1且全部为零的数字。

以下循环将对您有所帮助。

do {
    y = x;
    x = x&(x-1);
}while(x);
return y;

其中CCD_ 1是原始号码,而CCD_。

下面给出了无分支代码。

unsigned flp2(unsigned x) {
  x = x| (x>>1);
  x = x| (x>>2);
  x = x| (x>>4);
  x = x| (x>>8);
  x = x| (x>>16);
return x - (x>>1);
}

或者(依靠循环展开,这是所有现代编译器都能做到的)

unsigned log2(unsigned x)
{
  for (int i = 0; i < std::numeric_limits<unsigned>::digits; i*=2)
     x |= (x>>i);
  return x - (x>>1);
}

来源:Hacker’s Delight。

我不完全确定你要针对的是哪种积分类型,所以在尝试覆盖所有类型的要求下。。。

#include <iostream>
#include <cmath>
template<typename T>
typename std::enable_if<std::is_integral<T>::value,T>::type fn(T N)
{
    std::cout << __PRETTY_FUNCTION__ << std::endl;
    return T(1) << static_cast<T>(std::floor(std::log2(N)));
}
int main()
{
    std::cout << fn(4095) << std::endl;
    std::cout << fn(4096) << std::endl;
    std::cout << fn(4097) << std::endl;
    std::cout << fn(281474976710655) << std::endl;
    std::cout << fn(281474976710656) << std::endl;
    std::cout << fn(281474976710657) << std::endl;
    return 0;
}

输出

typename std::enable_if<std::is_integral<T>::value, T>::type fn(T) [T = int]
2048
typename std::enable_if<std::is_integral<T>::value, T>::type fn(T) [T = int]
4096
typename std::enable_if<std::is_integral<T>::value, T>::type fn(T) [T = int]
4096
typename std::enable_if<std::is_integral<T>::value, T>::type fn(T) [T = long]
140737488355328
typename std::enable_if<std::is_integral<T>::value, T>::type fn(T) [T = long]
281474976710656
typename std::enable_if<std::is_integral<T>::value, T>::type fn(T) [T = long]
281474976710656

在二进制(假设您是整数类型)中,2的最大幂是最高有效位

有几种方法可以有效地计算

最有效的方法是使用内置的,它使用可以在一个周期内执行的机器代码

效率最低的是循环去除最低比特的

如果你不能使用内置程序,我喜欢这里的一个:

unsigned int v;  // 32-bit value to find the log2 of 
const unsigned int b[] = {0x2, 0xC, 0xF0, 0xFF00, 0xFFFF0000};
const unsigned int S[] = {1, 2, 4, 8, 16};
int i;
register unsigned int r = 0; // result of log2(v) will go here
for (i = 4; i >= 0; i--) // unroll for speed...
{
  if (v & b[i])
  {
    v >>= S[i];
    r |= S[i];
  } 
}

您可以在以下位置找到更多类似内容:http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogLookup

此测试适用于32位有符号int,其中best被分配为与候选值最接近的2次方,x在4到20亿的域中:

for (int x = 4; x < (1<<31)-1; x++)
{
    int best = (int)(log((double)x)/log(2.0));
    int test = (int)pow((double)2, best);
    if (test > x || (test*2 <= x && test*2 > 0)) // test*2 < 0 when very large
        break; // fail
}