在C#中计算整数log2的最快方法是什么?

如何最有效地计算C#中整数(日志库2)所需的位数? 例如:

int bits = 1 + log2(100); => bits == 7 

代码行或运行时执行速度方面的效率?

代码很简单: Math.log(n, 2)

运行时速度有点棘手,但你可以通过一种“二分搜索”来实现:

 int bits = 1; for (int b = 16; b >=1; b/=2) { int s = 1 << b; if (n >= s) { n>>=b; bits+=b; } } 

我不是100%肯定我在那里有逻辑,但希望这个想法很明确。 .NET VM中可能存在一些开销,但原则上它应该更快。

for循环初始化器中的16是基于int所需位数的一半。 如果你正在使用多头,请在32等处开始,等等。

对Guffa的答案略有改进……由于你添加到结果中的数量总是2的幂,使用位操作可以在某些体系结构上产生轻微的改进。 此外,由于我们的上下文是位模式,因此使用hex会稍微更具可读性。 在这种情况下,将算术移动2的幂是有用的。

 int bits = 0; if (n > 0xffff) { n >>= 16; bits = 0x10; } if (n > 0xff) { n >>= 8; bits |= 0x8; } if (n > 0xf) { n >>= 4; bits |= 0x4; } if (n > 0x3) { n >>= 2; bits |= 0x2; } if (n > 0x1) { bits |= 0x1; } 

此外,应该添加对n == 0的检查,因为上面将产生0的结果并且Log(0)是未定义的(不管基数)。

在ARM组装中,该算法产生非常紧凑的代码,因为比较后的分支可以通过条件指令消除,这避免了管道刷新。 例如:

 if (n > 0xff) { n >>= 8; bits |= 0x8; } 

变为(令R0 = n,R1 =位)

 CMP R0, $0xff MOVHI R0, R0, LSR $8 ORRHI R1, R1, $0x8 

您可以简单地计算在值为零之前必须删除位的次数:

 int bits = 0; while (n > 0) { bits++; n >>= 1; } 

对于大数字更有效,您可以先计算一组位:

 int bits = 0; while (n > 255) { bits += 8; n >>= 8; } while (n > 0) { bits++; n >>= 1; } 

编辑:

最有效的方法是使用Flynn1179建议的二进制步骤(upvoted for inspiration :),但将循环扩展为硬编码检查。 这至少是上述方法的两倍,但也有更多代码:

 int bits = 0; if (n > 32767) { n >>= 16; bits += 16; } if (n > 127) { n >>= 8; bits += 8; } if (n > 7) { n >>= 4; bits += 4; } if (n > 1) { n >>= 2; bits += 2; } if (n > 0) { bits++; }