C#将多个有符号整数打包为一个64位值

Wil*_*ell 4 c# 64-bit

我需要在一个64位值中打包和解压几个值.我有3个有符号整数(x,y,z).我想将它们打包成一个64位值(有符号或无符号对我来说无关紧要),分别使用24,16和24位.这是我的要求:

1)我可以提前确保存储的值不超过我用来将它们存储到64位值的位数限制,因此不需要进行额外的检查.

2)初始值是有符号的,所以我认为可能需要做一些魔术才能确保没有任何东西丢失.

3)这种转换将发生很多,所以它需要很快.我知道在C++中,通过将值存储在指定整数长度的结构中,然后建立一个指针,该指针只指向可用于64位值的第一个值,可以很容易地完成.使用这种方法,确实没有任何数学需要完成,一切都只是内存读取或正确.据我所知,这不能简单地在C#中完成,但C#是我必须为这个项目工作的.

4)我真的不关心64位值是有符号还是无符号,只要我可以双向操作并恢复初始值,并且使用的任何类型都可以用于Dictionary键.

Mar*_*ell 10

面具和班次可能是你最好的选择.你可以在C#中创建显式的布局结构,但是没有24位原语,所以你不得不绊倒自己,不管怎样都必须屏蔽.一旦你转移,通常最好是无符号工作(特别是在右移时),所以:

ulong val = ((((ulong)x) & 0xFFFFFF) << 40) // 24 bits of x, left-shifted by 40
          | ((((ulong)y) & 0xFFFF) << 24) // 16 bits of y, left-shifted by 24
          | (((ulong)z) & 0xFFFFFF); // 24 bits of z, no left-shift
Run Code Online (Sandbox Code Playgroud)

并反转(假设我们想要uint值):

uint a = (uint)((val >> 40) & 0xFFFFFF),
     b = (uint)((val >> 24) & 0xFFFF),
     c = (uint)(val & 0xFFFFFF);
Run Code Online (Sandbox Code Playgroud)


das*_*ght 8

使用这种方法,确实没有任何数学需要完成,一切都只是内存读或写.

实际上,当你将部分整数设置为位域时,数学就完成了,所以还有很多数学运算.

据我所知,这不能简单地在C#中完成,但C#是我必须为这个项目工作的.

正确,在C#中,您需要编写将位组合成long手动的代码.假设你已经完成了范围检查,这是相对简单的:

static long Pack(long a24, long b16, long c24) {
    // a24 can go with no masking, because its MSB becomes
    // the MSB of the 64-bit number. The other two numbers
    // need to be truncated to deal with 1s in the upper bits of negatives.
    return a24<<40 | (b16&0xffffL)<<24 | (c24&0xffffffL);
}
static void Unpack(long packed, out int a24, out int b16, out int c24) {
    a24 = (int)(packed >> 40); // Sign extension is done in the long
    b16 = ((int)(packed >> 8)) >> 16; // Sign extension is done in the int
    c24 = ((int)(packed << 8)) >> 8;  // Sign extension is done in the int
}
Run Code Online (Sandbox Code Playgroud)

演示.


Han*_*ant 5

这些值在长整数内是字节对齐的,您希望利用Intel/AMD处理器能够直接访问它们以尽可能快地生成代码.杀手要求是24位大小,处理器只能直接读/写8位,16位,32位或64位.

这也是C++中的一个问题,你必须使用位字段.C#不支持它们,您必须编写C++编译器自动发出的代码.像这样:

[StructLayout(LayoutKind.Explicit)]
struct MyPackedLong {
    [FieldOffset(0)] uint item1;    // 24-bit field
    [FieldOffset(3)] uint item2;    // 24-bit field
    [FieldOffset(6)] ushort item3;  // 16-bit field

    public uint Item1 {
        get { return item1 & 0xffffff; }
        set { item1 = (item1 & 0xff000000) | value; }
    }
    public uint Item2 {
        get { return item2 & 0xffffff; }
        set { item2 = (item2 & 0xff000000) | value; }
    }
    public ushort Item3 {
        get { return item3; }
        set { item3 = value; }
    }
}
Run Code Online (Sandbox Code Playgroud)

这里有一些技巧,请注意item2故意偏移为3,以便不需要移位.我订购了字段,因此它们的访问是最佳的,将16位值放在第一位或最后一位是最好的.未经彻底测试,应该在球场.在线程代码中要小心,写入不是原子的.