接着阅读 lua 的源码,惊喜越来越多。比如今天看到了一个快速计算 log2(x) 的方法。代码如下:


int luaO_log2 (unsigned int x) {
  static const lu_byte log_2[256] = {
    0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
    6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
    7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
    7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
    8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
    8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
    8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
    8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8
  };
  int l = -1;
  while (x >= 256) { l += 8; x >>= 8; }
  return l + log_2[x];
}


想法也很朴素,就是把整数转换成 2^n 的形式,然后求出 n 就可以了。比如 256 是 2^8 ,所以这里数组 log_2[256] = 8,那些转换不成 2^n 的数也就被近似计算了,比如 255,我们知道 256 = 2^8,而 128 = 2^7,所以 128 和 256 之间的数都被近似成 256 了,结果也都是 8。我们可以看到 log_2[128] 到 log_2[255] 都等于 8。

这里误差也就是 1。完全可以忽略了。如果这个数大于 256,那么就让他除以 256,同时结果加 8 就可以了。最后的误差仍然是 1。

其实你也完全可以把这个 log_2 的数组设计的更大一些,这样需要进入 while 循环的条件就要求所求的数更大。作者定在 256 估计是考虑到在实际计算过程中对于大于等于 256 的数求 log2 已经很少用到了。这个 log_2 的数组你还可以设计的更合理一些,比如把 log_2[128] 到 log_2[255] 之间的数不全部设成 8,而是把靠近 128 的地方的数设成 7,把靠近 255 的地方设成 8,不过这样就又陷入到更麻烦的考虑中,比如到底把哪里开始设成 8,哪里设成 7 等等。本来是为了快速计算的一个函数,最后又变麻烦,必然不好。


最后我在网上搜索快速计算 log2 的方法的时候又看到了 Quake III 中的神奇代码。我不得不说,Quake III 真是一个神奇的引擎,里面有大量值得学习的代码,等我阅读完 lua 的源码之后下一步就是阅读它了。先来看看它是怎么进行 log2 计算的:


static const int debruijn[32] = {
     0,  1, 28,  2, 29, 14, 24,  3, 30, 22, 20, 15, 25, 17,  4,  8,
    31, 27, 13, 23, 21, 19, 16,  7, 26, 12, 18,  6, 11,  5, 10,  9
};
#define LOG2(X) (debruijn[((Uint32)(((X) & -(X)) * 0x077CB531U)) >> 27])


看到这段代码我真心都快被吓尿了。首先我只知道 (x) & (-x) 的意思是取出 x 末尾的 0 和第一次出现的 1。其他我就一概不明了。最神奇的是这里面又出现一个 magic number 0x077CB531U。上次在 Quake III 里看到的那个牛顿转换的初始值的那个 magic number 的时候我就尿了。这次不例外。还好在网上看到了 Matrix67 给出的解释,不过没有仔细看,等过段时间再看看。


Lua :lobject.c

Quake III:trick 代码

Matrix67:神秘常量复出!用0x077CB531计算末尾0的个数