我当然知道这样做的方法:
/// <summary>Gets the number of bits needed to represent the number.</summary>
public static int Size(int bits)
{
var size = 0;
while(bits != 0)
{
bits >>= 1;
size++;
}
return size;
}
因此Size(15)返回4,Size(16)返回5.
但我想(希望)有一种更快捷的方法。但我无法想象(或谷歌)一个漂亮而流畅(快速)的算法。
答案 0 :(得分:3)
首先,我真的怀疑这是你正在看的瓶颈。 (过早优化是所有邪恶的根源)
话虽如此,在这篇有趣的文章中有一些有趣的方法: Bit Twiddling Hacks
包括你采取的天真方法(展开确实有帮助):
unsigned int v; // 32-bit word to find the log base 2 of
unsigned int r = 0; // r will be lg(v)
while (v >>= 1) // unroll for more speed...
{
r++;
}
或者在O(log n)中的这个:
uint32_t v; // find the log base 2 of 32-bit v
int r; // result goes here
static const int MultiplyDeBruijnBitPosition[32] =
{
0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30,
8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31
};
v |= v >> 1; // first round down to one less than a power of 2
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
r = MultiplyDeBruijnBitPosition[(uint32_t)(v * 0x07C4ACDDU) >> 27];
还有一些。
但是请注意,C / C ++中的速度越快,C#中的速度就越慢。您的代码纯粹使用了一些实际上并不坏的局部变量,如果您想确保其他方法更好,请先对其进行基准测试。
答案 1 :(得分:2)
如果您要查找最高位集,请参阅Find first set。一种可能的实现(不处理零输入)是:
table[0..31] = {0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31} function lg_debruijn (x) for each y in {1, 2, 4, 8, 16}: x ← x | (x >> y) return table[(x * 0x07C4ACDD) >> 27]
如果要计算1位数(有时称为人口数),请查看Hamming weight。一种可能的解决方案是:
int popcount_4(uint64_t x) { int count; for (count=0; x; count++) x &= x-1; return count; }
有时此功能甚至支持语言:
一些C编译器提供了提供位计数功能的内在函数。例如,GCC(自2004年4月的3.4版本开始)包含内置函数
__builtin_popcount
,如果可用,将使用处理器指令,否则将使用高效的库实现。自2005年6月1.5版以来,LLVM-GCC已包含此功能。
答案 2 :(得分:2)
不是最快的,但可能是最短的一个:
public static int Size(int bits) {
return (int) (Math.Log(bits, 2)) + 1;
}
将while
转换为for
:
public static int Size(int bits) {
int size = 0;
for (; bits != 0; bits >>= 1)
size++;
return size;
}
答案 3 :(得分:2)
这不短,但它很快(在整个范围内平均)
internal static readonly byte[] msbPos256 = new byte[] {
255, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 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,
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,
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};
public static int SignificantBits(this int value) {
return HighBitPosition((uint)value) + 1;
}
public static int HighBitPosition(this ushort value) {
byte hiByte = (byte)(value >> 8);
if (hiByte != 0) return 8 + msbPos256[hiByte];
return (sbyte)msbPos256[(byte)value];
}
public static int HighBitPosition(this uint value) {
byte hiByte = (byte)(value >> 24);
if (hiByte != 0) return 24 + msbPos256[hiByte];
hiByte = (byte)(value >> 16);
return (hiByte != 0) ? 16 + msbPos256[hiByte] : HighBitPosition((ushort)value);
}
调用SignificantBits方法。需要注意的是,所有可能的int值中有99.6%将在前8个最高有效位(32个中)中具有最高有效位。这只需要一个右移操作,两个添加操作(其中一个可能由编译器优化到一个增量),一个!= 0测试和一个数组引用。因此,对于大多数可能的值,它非常快。要覆盖第二个8个最重要的位需要额外的右移,并且!= 0测试,并且它覆盖了99.998%的可能的int值。演员表花费不多。
您可以通过将msbPos256值增加1来削减+1操作。当我写这篇文章时,我对HighBitPosition函数比对SignficantBits函数更感兴趣,这就是为什么我按照我的方式做到了这一点(我在后面添加了一些重要的想法)。
IIRC,当我测试各种技巧时,这比我最初用于此的DeBruijn技术要快。 剪断的答案 4 :(得分:0)
我只是在此处添加此替代其他答案,尽管它们似乎已经解决了您的问题。
此方法使用位移计数前导零的数量,然后从32减去该值以找出剩余的空间。
execve()