WDUK
WDUK

Reputation: 1524

Bitwise unpacking a long into two numbers

I am struggling to unpack my data from a long type to two numbers. Not sure where i am going wrong.

I create a unique number from two numbers by packing them into a long:

    public static long Squeeze(float x, float y)
    {
        return ((long)x << 32) | (long)y;
    }

So the long consists for 4 bytes for x then 4 bytes for y.

Then i am trying to get the numbers back out with:

float x = (float)(hash >> 32);
float y = (float)(hash | int.MaxValue); // this should be 1111 1111 1111 1111 i think

But it doesn't seem to work, x appears to be correct, but y is giving me numbers that it should not.

Also it needs to work for negative numbers too.

Example:

(2.0, 9.0) => Packed: 8589934601 => Unpacked: (2, 1.073742E+10)
(-1.0, -1.0) => Packed: -1 => Unpacked: (-1.0, -2147484000.0)

Upvotes: 2

Views: 303

Answers (3)

Prince Owen
Prince Owen

Reputation: 1505

Here's an Endian agnostic version of Patrick Roberts' answer.

public static long PackLong(int x, int y)
{
    var bytes = new byte[8];

    if (BitConverter.IsLittleEndian)
    {
        BitConverter.GetBytes(x).CopyTo(bytes, 4);
        BitConverter.GetBytes(y).CopyTo(bytes, 0);
    }
    else
    {
        BitConverter.GetBytes(x).CopyTo(bytes, 0);
        BitConverter.GetBytes(y).CopyTo(bytes, 4);
    }


    return BitConverter.ToInt64(bytes, 0);
}

public static void UnpackLong(long value, out int x, out int y)
{
    var bytes = BitConverter.GetBytes(value);

    if (BitConverter.IsLittleEndian)
    {
        x = BitConverter.ToInt32(bytes, 4);
        y = BitConverter.ToInt32(bytes, 0);
    }
    else
    {
        x = BitConverter.ToInt32(bytes, 0);
        y = BitConverter.ToInt32(bytes, 4);
    }
}

A helper function like this can be quite misleading when Endianess is not accounted for.

Upvotes: 0

Patrick Roberts
Patrick Roberts

Reputation: 51886

You should use BitConverter instead of casting:

public static long Squeeze(float x, float y)
{
    var bytes = new byte[8];
    BitConverter.GetBytes(x).CopyTo(bytes, 0);
    BitConverter.GetBytes(y).CopyTo(bytes, 4);
    return BitConverter.ToInt64(bytes, 0);
}

and

public static void Unpack(long value, out float x, out float y)
{
    var bytes = BitConverter.GetBytes(value);
    x = BitConverter.ToSingle(bytes, 0);
    y = BitConverter.ToSingle(bytes, 4);
}

As long as you're not transferring the byte array out of the program, it doesn't matter whether the system uses little endian or big endian byte ordering. If you need to know, you can check BitConverter.IsLittleEndian.

Upvotes: 4

Renat
Renat

Reputation: 8962

You need & (bitwise and) instead of | (bitwise or) to extract by mask:

unchecked 
{
    ...
    float y = (float)(int)(hash & (long)uint.MaxValue)
 }

Upvotes: 4

Related Questions