如何有效地计算楼层日志基数2 ^(1/4)

时间:2014-08-28 20:57:56

标签: algorithm bit-manipulation numerical-methods

计算floor(log_2(x))可以通过计算有many个快速算法的零的数量来完成。

floor(log_{2^(1/4)}(x))是一个64位无符号int而不是所有可能的值时,是否有类似的计算x的技巧?

log_{2^(1/4)}(x)=4*log_2(x)=log_2(x^4)开始,这相当于为floor(4*log_2(x))floor(log(x^4))找到有效的算法。

1 个答案:

答案 0 :(得分:0)

在我们计算floor(log_2(x))之后,我们可以划分z = x / 2^floor(log_2(x))并考虑计算floor(log_{2^(1/4)}(z))的问题z属于范围[1, 2),因为{{ 1}}。 floor(log_{2^(1/4)}(x)) = 4 floor(log_2(x)) + floor(log_{2^(1/4)}(z))只有四种可能性,因此floor(log_{2^(1/4)}(z))与常量进行两次比较(三次无分支)

z

就足够了。为了完全避免浮点运算,“除法”可以实现为左移,使(2^(1/4))^1, (2^(1/4))^2, (2^(1/4))^3 与类似表示的常数进行比较。

现在使用C代码:

(2^63) z

用于计算常量的Mathematica / Wolfram Alpha查询是

#include <assert.h>
#include <stdio.h>

static int mylog(unsigned long long x) {
  assert(x > 0ULL);
  /* compute n = floor(log2(x)) */
  unsigned long long y = x | (x >> 1);
  y |= y >>  2;
  y |= y >>  4;
  y |= y >>  8;
  y |= y >> 16;
  y |= y >> 32;
  y -= (y >> 1) & 0x5555555555555555ULL;
  y = (y & 0x3333333333333333ULL) + ((y >> 2) & 0x3333333333333333ULL);
  y = (y + (y >>  4)) & 0x0f0f0f0f0f0f0f0fULL;
  y = (y + (y >>  8)) & 0x00ff00ff00ff00ffULL;
  y = (y + (y >> 16)) & 0x0000ffff0000ffffULL;
  y = (y + (y >> 32)) & 0x00000000ffffffffULL;
  int n = (int)y - 1;
  /* normalize x and use binary search to find the last two bits of the log */
  x <<= 63 - n;
  n <<= 2;
  if (x < 0xb504f333f9de6485ULL) {
    return x < 0x9837f0518db8a970ULL ? n     : n + 1;
  } else {
    return x < 0xd744fccad69d6af5ULL ? n + 2 : n + 3;
  }
}

int main(void) {
  unsigned long long x;
  while (scanf("%llu", &x) == 1) {
    printf("%d\n", mylog(x));
  }
}