如何获取int的位大小

时间:2016-08-22 19:59:26

标签: c# bit

我当然知道这样做的方法:

/// <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.

但我想(希望)有一种更快捷的方法。但我无法想象(或谷歌)一个漂亮而流畅(快速)的算法。

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技术要快。

这是从https://github.com/softwx/SoftWx.Numerics

剪断的

答案 4 :(得分:0)

我只是在此处添加此替代其他答案,尽管它们似乎已经解决了您的问题。

此方法使用位移计数前导零的数量,然后从32减去该值以找出剩余的空间。

execve()