C # Packing multiple signed integers into one 64-bit value

I need to pack and unzip multiple values ​​to / from one 64-bit value. I have 3 signed integers (x, y, z). I would like to pack them into one 64-bit value (signed or unsigned doesn't matter to me) using 24, 16 and 24 bits for the values ​​respectively. Here are my requirements:

1) I can precede that the saved values ​​do not exceed the limits of the number of bits that I use to save them in a 64-bit value, so no additional checks are required.

2) The initial values ​​are signed, so I think some bit magic may be needed to ensure that nothing is lost.

3) This conversion will go through LOT, so it should be fast. I know that in C ++ it is quite easy to do this by storing the values ​​in a structure that sets an integer length, and then sets a pointer that just points to the first value that can be used for a 64-bit value. With this method, there really is no math that needs to be done, all just reading memory or right. As far as I can tell, this is not so easy to do in C #, but C # is what I need to work with for this project.

4) I don’t care if the 64-bit value is signed or unsigned, if I can go in both directions using the operation and restore the initial values, and any type is used for the dictionary key.

+4
source share
3 answers

With this method, there really is no math that needs to be done, all just reading or writing to memory.

Not really, the math is done when you set partial integers to bit fields, so quite a lot of math happens.

As far as I can tell, this is not so easy to do in C #, but C # is what I need to work with for this project.

That's right, in C # you will need to write code that combines bits in longmanually. Assuming you took care of the range check, this is relatively simple:

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
}

Demo version

+8
source

- , , . #, 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

(, uint):

uint a = (uint)((val >> 40) & 0xFFFFFF),
     b = (uint)((val >> 24) & 0xFFFF),
     c = (uint)(val & 0xFFFFFF);
+10

, Intel/AMD, , . - 24 , / 8, 16, 32 64 .

++, -. # , , ++ . :

[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; }
    }
}

Here are a few tricorams, note that it item2has an intentional offset of 3, so no shift is required. I ordered the fields so that their access is optimal, and it is best to use a 16-bit value, both the first and the last. Not thoroughly tested, must be in the football field. Be careful in stream code; entries are not atomic.

+5
source

Source: https://habr.com/ru/post/1680718/


All Articles