【问题标题】:Efficient computation of a power of 2高效计算 2 的幂
【发布时间】:2013-03-09 16:08:45
【问题描述】:

我需要将k 计算为 2 的最小幂,即 >= 整数值 nn 始终 > 0)

目前我正在使用:

#define log2(x) log(x)/log(2)
#define round(x) (int)(x+0.5)

k = round(pow(2,(ceil(log2(n)))));

这是一个性能关键的功能

有没有更高效的计算方式来计算k

【问题讨论】:

  • 如果你使用 GCC,你可以使用1 << CHAR_BIT * sizeof x - __builtin_clz(x),前提是x 的类型为unsigned int,或者,在普通系统上,intunsigned long 也有 __builtin_clzl。一些非 GCC 编译器也支持这个扩展。这将比迄今为止在具有“查找第一位集”指令的处理器上的任何其他答案都快。
  • 注意编辑从'>一个整数值'到'>=一个整数值'
  • 现在每个人都必须再次更改他们的答案。 :-)
  • 要求堆栈溢出“撤消”按钮...
  • 与答案无关,但值得一提的是任何遇到此问题并且不知道更好的人:明智的做法是放弃 +0.5 和 (int) 演员,而是使用 floor() 或像这样的计算中的上限()函数。当然,对数仍然很慢。

标签: c performance math optimization


【解决方案1】:
/* returns greatest power of 2 less than or equal to x, branch-free */
/* Source: Hacker's Delight, First Edition. */
int
flp2(int 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);
}

研究它并看看它是如何工作的很有趣。我认为让您确定您所看到的哪种解决方案最适合您的情况的唯一方法是在文本夹具中使用所有这些解决方案并对其进行分析,看看哪种解决方案最适合您的目的。

由于是无分支的,因此相对于其他一些而言,它的性能可能相当不错,但您应该直接对其进行测试以确定。

如果您想要大于或等于 X 的 2 的最小幂,您可以使用稍微不同的解决方案:

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

【讨论】:

  • 这很好,因为“最左边”1 是目标的答案,所有其他位都为 0,这很好。对于 64 位数字,您将需要额外的一行。
  • 啊,是的,你想要别的东西,让我贴出来。
  • 如果你使用x |= x >>1; ...形式,你可以去掉括号,因为赋值运算符的优先级很低。
  • 是的。将最终结果向上或向下移动 2 的一次幂(取决于实际意图)非常简单。希望 Hiett 能够将这些信息用于他的程序的特定需求。
  • @Hiett 因为右移 0 仍然是 0,如果您需要同时满足 32 位和 64 位的需求,可以添加额外的行。 32 位的结果仍然是正确的,只会浪费一些指令
【解决方案2】:
int calculate_least_covering_power_of_two(int x)
{
  int k = 1;
  while( k < x ) k = k << 1;
  return k;
}

【讨论】:

  • 用无分支版本对此进行基准测试,我怀疑您会发现这不是最有效的方法。但是,它会起作用。
  • @RandyHoward 我已经做到了,并且没有明显的区别(对于我的应用程序),所以我想我会坚持这个答案,因为它更简单
  • @Randy:这种幼稚的方法很容易理解,一定不能移植到 64- (128-, ...) 位机器上。首先理解它也很有帮助,然后再转到无分支版本。 无分支版本肯定对于均匀分布的 32 位整数更有效。,但对于小整数可能会慢一些。另外,我给你的帖子加了一个“+1”,我的帖子被接受真是一个惊喜)
  • @alex-shesterov 你能解释一下将你的函数移植到 64 位机器的问题吗?
  • @Hiett:this 答案中的函数适用于 16、32、64 位系统,无需任何更改;它的复杂性(迭代次数)等于系统位数(即 16、32、64 或其他)在最坏的情况下 - 即它的位数是线性的。 RandyHoward 的答案中的函数需要通过添加x |= x&gt;&gt;32 移植到 64 位平台,其复杂性是对数在最坏情况下和最好情况下。我的估计是 RandyHoward 的函数在 32 位系统上平均比这个函数快 15%。
【解决方案3】:
lim = 123;
n = 1;
while( ( n = n << 1 ) <= lim );

将你的数字乘以 2,直到它大于 lim。

一个左移一个值乘以 2。

【讨论】:

  • @EricPostpischil Tnx 用于注意,已修复...(不适用于 lim = 零,但可以使用 if() 解决)
【解决方案4】:

是的,您可以通过简单地获取相关数字并使用位移来确定 2 的幂来计算。
右移取数字中的所有位并将它们向右移动,删除最右边(最低有效位)的数字。它相当于执行整数除以 2。左移一个值将所有位向左移动,删除左端移位的位,并在右端添加零,有效地将值乘以 2。 因此,如果您计算在数字达到零之前需要右移多少次,您已经计算出以 2 为底的对数的整数部分。然后通过多次左移值 1 来使用它来创建结果。

  int CalculateK(int val)
  {
      int cnt = 0;
      while(val > 0)
      {
           cnt++;
           val = val >> 1;
      }
      return 1 << cnt;
  }

编辑:或者,更简单一点:您不必计算计数

  int CalculateK(int val)
  {
      int res = 1;
      while(res <= val) res <<= 1;
      return res ;
  }

【讨论】:

    【解决方案5】:
    k = 1 << (int)(ceil(log2(n)));
    

    您可以利用二进制数字表示 2 的幂这一事实(1 是 1,10 是 2,100 是 4,等等)。将 1 左移 2 的指数会得到相同的值,但速度要快得多。

    虽然如果你能以某种方式避免 ceil(log2(n)) 你会看到更大的性能提升。

    【讨论】:

    • 此代码无法编译,因为ceil 的类型为double,不能与&lt;&lt; 一起使用。当插入到 int 的强制转换并且 n 为 32 时,此代码生成 32,但所需的结果是 64。
    • 我在示例中添加了对 int 的强制转换。我不确定为什么 n=32 的期望结果会是 64……也许我不明白原来的问题。
    • @EricPostpischil,它似乎要求大于或等于 n 的 2 的最小幂。 32 等于 32。但也许我还是误会了。无论哪种方式,兰迪霍华德的答案都更好。
    【解决方案6】:

    来源:hackersdelight.org

    /* altered to: power of 2 which is greater than an integer value */
    
    unsigned clp2(unsigned x) {
       x = x | (x >> 1);
       x = x | (x >> 2);
       x = x | (x >> 4);
       x = x | (x >> 8);
       x = x | (x >>16);
       return x + 1;
    }
    

    请记住,您需要添加:

    x = x | (x >> 32);
    

    对于 64 位数字。

    【讨论】:

      猜你喜欢
      • 2018-05-28
      • 2013-04-06
      • 2011-05-05
      • 2014-09-10
      • 2011-11-14
      • 2019-12-05
      • 2014-02-21
      • 2015-04-30
      • 2014-08-21
      相关资源
      最近更新 更多