舍入值为最接近的2的幂

时间:2015-08-13 20:20:38

标签: c#

我正在寻找C#中最快的方法来将值舍入到最接近的2的幂。 我已经发现,如果使用像这样的按位运算符,最好的方法是将值舍入到下一个2的幂。

int ToNextNearest(int x)
{
    if (x < 0) { return 0; }
    --x;
    x |= x >> 1;
    x |= x >> 2;
    x |= x >> 4;
    x |= x >> 8;
    x |= x >> 16;
    return x + 1;
}

但是这给了下一个最接近而不是最近的,我想只有最近的2的幂。 这是一种简单的方法。

int ToNearest(int x)
{
    Math.Pow(2, Math.Round(Math.Log(x) / Math.Log(2)));
}

但是有没有一个更好的优化版本找到最接近的两个值?

非常感谢。

7 个答案:

答案 0 :(得分:7)

当然,最好的方法是使用您的按位例程来查找下一个2的幂,然后将该结果除以2。这为您提供了之前的两个幂。然后,一个简单的比较将告诉你哪两个更接近。

int ToNearest(int x)
{
  int next = ToNextNearest(x);
  int prev = next >> 1;
  return next - x < x - prev ? next : prev;
}

未经测试的代码,但您明白了。

答案 1 :(得分:1)

这个怎么样:

int ToNearest(int val, int pow)
{
    if (pow < 0) return 0;
    if (pow == 0) return val;

    if (val & (1 << (pow - 1))) {
        return ((val >> pow) + 1) << pow;
    } else {
        return (val >> pow) << pow;
    }
}

答案 2 :(得分:0)

尚未测试,但我认为这可行

int ToNearest(value x)
{
    int num = 0;
    for(int i=1; i < 65; i++)
    {
        int cur = Math.Abs(value - 0<<i);
        if(Math.Abs(value - 0<<i) < Math.Abs(value - num))
            num = cur;
        else if(num != 0) break;
    }
    return num;
}

答案 3 :(得分:0)

这是@john的建议解决方案的完整实现,如果值正好在下一个和前一个2的幂之间,它将会向上舍入。

public static int RoundToNextPowerOfTwo(int a)
{
    int next = CeilToNextPowerOfTwo(a);
    int prev = next >> 1;
    return next - a <= a - prev ? next : prev;
}

public static int CeilToNextPowerOfTwo(int number)
{
    int a = number;
    int powOfTwo = 1;

    while (a > 1)
    {
        a = a >> 1;
        powOfTwo = powOfTwo << 1;
    }
    if (powOfTwo != number)
    {
        powOfTwo = powOfTwo << 1;
    }
    return powOfTwo;
}

答案 4 :(得分:0)

由于C#需要IEEE754浮点数,因此在任何不能模拟浮点函数的平台上可能有更快的方法:

int ToNearestPowerOf2(int x) =>
  1 << (int)(BitConverter.DoubleToInt64Bits(x + x/3) >> 52) - 1023;

理由:

  • x + x/3
    最接近2的幂,基本上是* 4/3

  • (BitConverter.DoubleToInt64Bits(x) >> 52) - 1023
    采用浮点指数(ln2(x))

  • 1 << x
    基数为2的指数函数

该函数显然需要x的正值。 0不会起作用,因为2的最接近的幂是-∞, 负值具有复数对数。

这是否是最快的方式可能在很大程度上取决于JIT优化器从代码中挤出的内容,更具体地说是它如何处理DoubleToInt64Bits中的硬指针。这可能会阻止其他优化。

您不必使用任何比较来获得最接近的2次幂。由于2的所有幂都由相同因子分开,因此舍入点始终为下一次幂的3/4 2(即确切地设置最顶部的2位)。因此乘以倒数然后截断就可以完成这项工作。

答案 5 :(得分:0)

我正在使用这个

public static int CeilPower2(int x)
{
    if (x < 2) {
        return 1;
    }
    return (int) Math.Pow(2, (int) Math.Log(x-1, 2) + 1);
}

public static int FloorPower2(int x)
{
    if (x < 1) {
        return 1;
    }
    return (int) Math.Pow(2, (int) Math.Log(x, 2));
}

答案 6 :(得分:0)

在.Net Core上,执行此操作的最快方法可能是使用内部函数:

private static int NearestPowerOf2(uint x)
{
    return 1 << (sizeof(uint) * 8 - BitOperations.LeadingZeroCount(x - 1));
}

在支持LZCNT指令的CPU上,它只有6条CPU指令,没有分支。