将范围转换为位数组

时间:2009-03-27 02:29:30

标签: c# bit-manipulation bitarray

我正在用C#编写一个时间要求严格的代码片段,要求我将定义包含范围的两个无符号整数转换为位字段。例如:

uint x1 = 3;
uint x2 = 9;
  //defines the range [3-9]
  //                              98  7654 3
  //must be converted to:  0000 0011  1111 1000

以相反的顺序显示位可能有帮助

此范围的最大值是在运行时给出的参数,我们称之为max_val。因此,位字段变量应该定义为UInt32数组,其大小等于max_val/32

UInt32 MAX_DIV_32 = max_val / 32;
UInt32[] bitArray = new UInt32[MAX_DIV_32];

给定变量x1x2定义的范围,执行此转换的最快方法是什么?

6 个答案:

答案 0 :(得分:2)

试试这个。计算必须用所有1填充的数组项的范围,并通过迭代此范围来执行此操作。最后在两个边界设置项目。

Int32 startIndex = x1 >> 5;
Int32 endIndex = x2 >> 5;

bitArray[startIndex] = UInt32.MaxValue << (x1 & 31);

for (Int32 i = startIndex + 1; i <= endIndex; i++)
{
   bitArray[i] = UInt32.MaxValue;
}

bitArray[endIndex] &= UInt32.MaxValue >> (31 - (x2 & 31));

可能代码不是100%正确,但这个想法应该有用。


刚刚测试过,发现了三个漏洞。起始索引处的计算需要mod 32,并且在结束索引处,32必须是31并且逻辑而不是用于处理开始和结束索引的情况相同的赋值。应该很快。


只需在数组上以x1和x2的相等分布对其进行基准测试。 英特尔酷睿2双核E8400 3.0 GHz,MS VirtualPC与Windows 2003主机上的Server 2003 R2。

Array length [bits]           320         160         64
Performance [executions/s]    33 million  43 million  54 million

再一次优化x%32 == x&amp; 31但我无法确保获得业绩增长。由于我的测试中只有10.000.000次迭代,因此波动很大。我在VirtualPC中运行,使情况变得更加难以预测。

答案 1 :(得分:2)

我将BitArray中的整个位范围设置为true或false的解决方案:

public static BitArray SetRange(BitArray bitArray, Int32 offset, Int32 length, Boolean value)
{

    Int32[] ints = new Int32[(bitArray.Count >> 5) + 1];

    bitArray.CopyTo(ints, 0);

    var firstInt = offset >> 5;
    var lastInt = (offset + length) >> 5;

    Int32 mask = 0;

    if (value)
    {
        // set first and last int
        mask = (-1 << (offset & 31));
        if (lastInt != firstInt)
            ints[lastInt] |= ~(-1 << ((offset + length) & 31));
        else
            mask &= ~(-1 << ((offset + length) & 31));

        ints[firstInt] |= mask;

        // set all ints in between
        for (Int32 i = firstInt + 1; i < lastInt; i++)
            ints[i] = -1;
    }

    else
    {
        // set first and last int
        mask = ~(-1 << (offset & 31));
        if (lastInt != firstInt)
            ints[lastInt] &= -1 << ((offset + length) & 31);
        else
            mask |= -1 << ((offset + length) & 31);

        ints[firstInt] &= mask;

        // set all ints in between
        for (Int32 i = firstInt + 1; i < lastInt; i++)
            ints[i] = 0;

    }

    return new BitArray(ints) { Length = bitArray.Length };

}

答案 2 :(得分:0)

你可以尝试:

UInt32 x1 = 3;
UInt32 x2 = 9;
UInt32 newInteger = (UInt32)(Math.Pow(2, x2 + 1) - 1) & 
                   ~(UInt32)(Math.Pow(2, x1)-1);

答案 3 :(得分:0)

是否有理由不使用System.Collections.BitArray类而不是UInt32 []?否则,我会尝试这样的事情:

int minIndex = (int)x1/32;
int maxIndex = (int)x2/32;
// first handle the all zero regions and the all one region (if any)
for (int i = 0; i < minIndex; i++) {
    bitArray[i] = 0;
}
for (int i = minIndex + 1; i < maxIndex; i++) {
    bitArray[i] = UInt32.MaxValue; // set to all 1s
}
for (int i = maxIndex + 1; i < MAX_DIV_32; i++) {
    bitArray[i] = 0;
}

// now handle the tricky parts
uint maxBits = (2u << ((int)x2 - 32 * maxIndex)) - 1; // set to 1s up to max
uint minBits = ~((1u << ((int)x1 - 32 * minIndex)) - 1); // set to 1s after min

if (minIndex == maxIndex) {
    bitArray[minIndex] = maxBits & minBits;
}
else {
    bitArray[minIndex] = minBits;
    bitArray[maxIndex] = maxBits;
}

答案 4 :(得分:0)

我很无聊,尝试使用char数组并使用Convert.ToUInt32(string, int)从基数2转换为uint

uint Range(int l, int h)
{
  char[] buffer = new char[h];
  for (int i = 0; i < buffer.Length; i++)
  {
    buffer[i] = i < h - l ? '1' : '0';
  }
  return Convert.ToUInt32(new string(buffer), 2);
}

一个简单的基准测试表明,我的方法比Angrey Jim快5%左右(即使你用一点位移替换第二个Pow。)

如果上限太大而无法放入单个uint,则转换为生成int数组可能最容易。这有点神秘,但我相信它有效。

uint[] Range(int l, int h)
{
  char[] buffer = new char[h];
  for (int i = 0; i < buffer.Length; i++)
  {
    buffer[i] = i < h - l ? '1' : '0';
  }

  int bitsInUInt = sizeof(uint) * 8;
  int numNeededUInts = (int)Math.Ceiling((decimal)buffer.Length /
                                         (decimal)bitsInUInt);
  uint[] uints = new uint[numNeededUInts];
  for (int j = uints.Length - 1, s = buffer.Length - bitsInUInt;
       j >= 0 && s >= 0;
       j--, s -= bitsInUInt)
  {
    uints[j] = Convert.ToUInt32(new string(buffer, s, bitsInUInt), 2);
  }

  int remainder = buffer.Length % bitsInUInt;
  if (remainder > 0)
  {
    uints[0] = Convert.ToUInt32(new string(buffer, 0, remainder), 2);
  }

  return uints;
}

答案 5 :(得分:0)

试试这个:

uint x1 = 3;
uint x2 = 9;

int cbToShift = x2 - x1; // 6
int nResult = ((1 << cbToShift) - 1) << x1; 

/*
  (1<<6)-1 gives you 63 = 111111, then you shift it on 3 bits left
*/