1

I am porting some C++ samples to Java

I am now stuck trying to pack four integers into a single one in the following layout [10, 10, 10, 2] in bits, that is the first int will occupy the first 10 bits, similar for the second and third one, while the last one just the last two bits.

In the C++ sample, this is the code to pack them:

GLM_FUNC_QUALIFIER uint32 packSnorm3x10_1x2(vec4 const & v)
    {
        detail::i10i10i10i2 Result;
        Result.data.x = int(round(clamp(v.x,-1.0f, 1.0f) * 511.f));
        Result.data.y = int(round(clamp(v.y,-1.0f, 1.0f) * 511.f));
        Result.data.z = int(round(clamp(v.z,-1.0f, 1.0f) * 511.f));
        Result.data.w = int(round(clamp(v.w,-1.0f, 1.0f) *   1.f));
        return Result.pack;
    }

Result.data is the following struct:

union i10i10i10i2
    {
        struct
        {
            int x : 10;
            int y : 10;
            int z : 10;
            int w : 2;
        } data;
        uint32 pack;
    };

With an input equal to [-1f,-1f,0f,1f] we get a Result.data equal to [-511,-511,0,1] and this return value 1074267649, that, in binary is:

        0                       -511
   |          |             |          |
 0100 0000 0000 1000 0000 0110 0000 0001
 ||             |          |
 1                  -511

What I did so far is:

public static int packSnorm3x10_1x2(float[] v) {
    int[] tmp = new int[4];
    tmp[0] = (int) (Math.max(-1, Math.min(1, v[0])) * 511.f);
    tmp[1] = (int) (Math.max(-1, Math.min(1, v[1])) * 511.f);
    tmp[2] = (int) (Math.max(-1, Math.min(1, v[2])) * 511.f);
    tmp[3] = (int) (Math.max(-1, Math.min(1, v[3])) * 1.f);
    int[] left = new int[4];
    left[0] = (tmp[0] << 22);
    left[1] = (tmp[1] << 22);
    left[2] = (tmp[2] << 22);
    left[3] = (tmp[3] << 30);
    int[] right = new int[4];
    right[0] = (left[0] >> 22);
    right[1] = (left[1] >> 12);
    right[2] = (left[2] >> 2);
    right[3] = (left[3] >> 0);
    return right[0] | right[1] | right[2] | right[3];
}

tmp is [-511,-511,0,1], left is [-2143289344,-2143289344,0,1073741824], which in binary is:

[1000 0000 0100 0000 0000 0000 0000 0000,
 1000 0000 0100 0000 0000 0000 0000 0000,
 0000 0000 0000 0000 0000 0000 0000 0000,
 0100 0000 0000 0000 0000 0000 0000 0000]

And it makes sense so far. Now that I cleaned the value on the left, I want to drag them on the right at their right place. But when I do so, I get the gap on the left filled with 1s, I guess because of the signed int in java(?).

Then right is [-511,-523264,0,1073741824] or:

[1111 1111 1111 1111 1111 1110 0000 0001,
 1111 1111 1111 1000 0000 0100 0000 0000,
 0000 0000 0000 0000 0000 0000 0000 0000,
 0100 0000 0000 0000 0000 0000 0000 0000]

So, why is this happening and how can I fix this? Maybe with ANDing only the bits I am interested in?

elect
  • 6,765
  • 10
  • 53
  • 119

2 Answers2

7

The unsigned right shift operator >>> shifts a zero into the leftmost position.

Source: https://docs.oracle.com/javase/tutorial/java/nutsandbolts/op3.html

BlueMoon93
  • 2,910
  • 22
  • 39
  • Oh man, I didn't know that, thanks.. so simple.. crap, I can't accept it right now, I have to wait 8m... – elect Dec 18 '15 at 09:42
0
    struct
    {
        int x : 10;
        int y : 10;
        int z : 10;
        int w : 2;
    } data;

This code is completely non-portable, that is, if it even works as expected on your current system.

There is absolutely no way for you to tell what this struct will contain. You can't know what is MSB, you can't know how signedness will be treated, you can't know the endianess, you can't know if there will be padding etc etc. See this.

The only portable solution is to use a raw uint32_t and shift values into place.

Community
  • 1
  • 1
Lundin
  • 195,001
  • 40
  • 254
  • 396