我正在用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];
给定变量x1
和x2
定义的范围,执行此转换的最快方法是什么?
答案 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
*/