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

C#将多个有符号整数打包为单个64位值,c#,64-bit,C#,64 Bit,我需要在单个64位值之间打包和解包多个值。我有3个带符号的整数(x,y,z)。我想分别使用24位、16位和24位将它们打包成一个64位值(有符号或无符号对我来说无关紧要)。以下是我的要求: 1) 我可以提前确保存储的值不会超过我用于将它们存储到64位值中的位数限制,因此不需要进行额外的检查 2) 初始值是有符号的,所以我想可能需要做一些魔术来确保没有任何损失 3) 这种转换将要进行很多次,所以它需要很快。我知道在C++中,通过在一个指定整数长度的结构中存储这些值,然后建立一个指针,它指向的是可以

我需要在单个64位值之间打包和解包多个值。我有3个带符号的整数(x,y,z)。我想分别使用24位、16位和24位将它们打包成一个64位值(有符号或无符号对我来说无关紧要)。以下是我的要求:

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

2) 初始值是有符号的,所以我想可能需要做一些魔术来确保没有任何损失

3) 这种转换将要进行很多次,所以它需要很快。我知道在C++中,通过在一个指定整数长度的结构中存储这些值,然后建立一个指针,它指向的是可以用于64位值的第一个值。使用这种方法,实际上不需要做任何数学运算,一切都只是内存读取或正确。据我所知,这不能简单地用C#来完成,但C#是我在这个项目中必须使用的


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

掩码和移位可能是最好的选择。您可以在C#中创建显式布局结构,但没有24位原语,因此您可能会被自己绊倒,并且无论如何都必须屏蔽。一旦你要换位,通常最好不带符号地工作(尤其是右移时),因此:

使用这种方法,实际上不需要做任何数学运算,一切都只是内存读或写

不完全是这样,当你将部分整数设置到位域中时,数学就完成了,所以有相当多的数学在进行

据我所知,这不能简单地用C#来完成,但C#是我在这个项目中必须使用的

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

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
}
静态长包装(长a24、长b16、长c24){
//a24可以不使用掩蔽,因为它的MSB变为
//64位数字的MSB。其他两个数字
//需要截断以处理负片高位中的1。
返回a24(8))>>16;//符号扩展在int中完成
c24=((int)(packed>8;//符号扩展在int中完成
}

这些值在long中是字节对齐的,您需要利用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; }
    }
}

这里有一些技巧,请注意
item2
有一个3的故意偏移量,因此不需要移位。我对字段进行了排序,以便它们的访问是最佳的,将16位值放在第一位或最后一位是最好的。没有经过彻底测试,应该在大致范围内。在线程化代码中要小心,写入不是原子的。

这只是一个重复requirements dump,这不是问题。您是否尝试过使用逐位运算的解决方案,如果是,您遇到了什么问题?因此,创建一个
struct
,它保存一个
long
值。我们在Noda Time中做了类似的事情。()在这个问题上,你到底想问什么?可能使用
union
struct
作为一个成员(
uint64
),很清楚OP想要问什么。投票重新打开。另一个很好的答案……同意;这是一些非常巧妙的摆弄,以使缩小投射功能成为遮罩(尤其是
c24
)。我个人更喜欢“明显性”对于显式掩码,但我怀疑它们的性能会非常相似。不过,这种方法对于有符号数据特别好;就像它一样。哇,这和Marc Gravell的答案都很好,但是,因为这一个直接解决了对有符号值的需要,我将接受它。我感谢您在这方面的帮助。
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
}
[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; }
    }
}