我试图在C#中将以下数字编码为单个64位长:
结果数字的结构应包含具有固定位大小的编码值,因此我可以轻松解码它(例如,从数字的第13位开始,从6位开始解码,因为前12位是为年保留的。) / p>
到目前为止,我没有经常使用按位操作,所以我有点挣扎,我想出了以下代码来执行此操作:
private static long AddBitwise(long to, int toAdd, int startPosition, int maxLengthInBits)
{
var filledNumber = (1 >> maxLengthInBits) | toAdd;
to |= filledNumber << startPosition;
return to;
}
然后我这样称它来编码所有值:
private static long CalculateMaxBinary(int a, int b, int c, int d, int e, int f, int g, int h, int i)
{
long result = 0;
result = AddBitwise(result, a, 52, 12);
result = AddBitwise(result, b, 47, 5);
result = AddBitwise(result, c, 41, 6);
result = AddBitwise(result, d, 35, 6);
result = AddBitwise(result, e, 28, 7);
result = AddBitwise(result, f, 23, 5);
result = AddBitwise(result, g, 17, 6);
result = AddBitwise(result, h, 9, 8);
result = AddBitwise(result, i, 1, 8);
return result;
}
但是我必须做错事或采取完全错误的方法,有人能给我一个如何在指定位置设置特定固定位数的例子吗?
答案 0 :(得分:2)
我一般认为编码这样的值是不可取的。 .NET Designers经历了很多麻烦,所以我们永远不必处理“它在内存中的表现如何”,而且从我的原生C ++体验中我认为我们应该避免这个级别。
通常,用一堆8位,16位和32位整数构造一个结构就足够了,并保留它。你只能通过将9个7位值压缩到63位而不是只有9个8位值(72位)来节省那么多。 通常它会花费你更多的代码可读性,内存和CPU时间进行解码和编码,然后这个最小的节省(9位)是值得的。
如果每个实例的9位甚至对内存限制都很重要,那么听起来你应该改变设计的其他部分,这样你就不需要那么多的内存/许多实例开始了。您很可能陷入XY Problem,您认为此优化是解决方案。
答案 1 :(得分:1)
这绝对是可能的,而且实际上比您尝试的更容易。你需要注意在左移之前将数字设为long
,否则比特可能会丢失或者数字可能会移动错误的数量(请记住,移位计数是取模数左操作数的大小)或两者。
但你需要的是投射到long
,向左移动,或者将其转换成你拥有的东西:
private static long AddBitwise(long to, int toAdd, int pos)
{
return to | ((long)toAdd << pos);
}
此处不需要此尺寸,但您可以使用尺寸自动更新位置:
private static long prependBitwise(long to, int value, ref int pos, int size)
{
pos -= size;
return to | ((long)value << pos);
}
像这样使用:
int pos = 64;
long packed = 0;
packed = prependBitwise(packed, year, ref pos, 12);
packed = prependBitwise(packed, month, ref pos, 4);
packed = prependBitwise(packed, day, ref pos, 5);
// etc
顺便提一下,你的大部分位域都是超大的。要表示[1..31]中的日期数,只需要5位。 32不是真正的一天,但即使它仍然只需要6位,而不是7位。
还有其他策略,例如仍然包装&#34;顶部字段到底部字段&#34;但随着我们前进,将填充的长左移动,最终将底部字段留在底部位(而不是顶部位中的顶部字段),只是一个不同的对齐方式:
private static long prependBitwise(long to, int value, int size)
{
return (to << size) | value;
}
这有点好,因为它不需要那个丑陋的by-ref pos
,并且只有3个参数就像第一个版本一样(但是它没有让意外重叠字段的弱点) ),如果没有完全填充,它往往会使得到的包装长得更小(更低的值)。
注意填充顶部位(又名&#34;符号位&#34;)很好,你可以像普通位一样对待它,但是当解码时必须要小心提取最顶部位域的方法将使其具有负值。切换到ulong
会阻止此类意外。
答案 2 :(得分:0)
如果你真的必须这样做,你可以采取一些纠结:
public static ulong SetBits(ulong value, int bitOffsetInOutput, int inputBits, int inputBitCount)
{
ulong outputMask = (1ul << bitOffsetInOutput+inputBitCount) - (1ul << bitOffsetInOutput);
ulong inputMask = (1ul << inputBitCount) - 1ul;
return (value & ~outputMask) | (((ulong)inputBits & inputMask) << bitOffsetInOutput);
}
这使您可以将给定64位数的指定位范围设置为从32位数中获取的指定位数,而不会影响64位数中的任何其他位。
请注意,这不会设置最高位 - 但是您说只有63位数据,所以这不应该是一个问题。
测试程序:
using System;
namespace Demo
{
class Program
{
static void Main()
{
ulong value = 0;
value = SetBits(value, 8, 0b111111111111, 6);
// Expected result = 11111100000000
Console.WriteLine(Convert.ToString((long)value, 2));
value = SetBits(value, 17, 0xFFFF, 7);
// Expected result = 111111100011111100000000
Console.WriteLine(Convert.ToString((long)value, 2));
value = SetBits(value, 19, 0, 2);
// Expected result = 111001100011111100000000
Console.WriteLine(Convert.ToString((long)value, 2));
}
public static ulong SetBits(ulong value, int bitOffsetInOutput, int inputBits, int inputBitCount)
{
ulong outputMask = (1ul << bitOffsetInOutput+inputBitCount) - (1ul << bitOffsetInOutput);
ulong inputMask = (1ul << inputBitCount) - 1ul;
return (value & ~outputMask) | (((ulong)inputBits & inputMask) << bitOffsetInOutput);
}
}
}