Trouble understanding bitmasking

Recently I started trying to teach myself bitmasking, and I haven’t had much success. In my current game, I am trying to pack multiple variables of data type short into a single long. In addition to getting the below code to function properly, I had a few questions:

  • I have heard that primitive types in Java reserve the last bit to the sign of the variable (plus or minus). Is this true?
  • Java shorts are 2 Bytes, and longs are 8 Bytes. Should I then in theory be able to pack 4 shorts into 1 long? Will the different data types cause problems?

Below are my broken methods for packing/unpacking the shorts. I have an understanding of the bitwise operators and thier functions (although I’m a bit fuzzy on the difference between >> and >>>), so it’s more of the actual implementation that is confusing me.
Here is my packing method (components[] is guaranteed to be of length SHORTS_IN_PACK):

private static final long packShorts(short[] components) {
		//should initialize pack to all zero bits
		long pack = Long.MIN_VALUE;

		for (int i=0; i < SHORTS_IN_PACK; i++)
			pack |= (components[i] & 0xFFFF) << (i*16);

		return pack;
}

And here is my unpacking method:

private static final short[] unpackShorts(long pack) {
		short[] components = new short[SHORTS_IN_PACK];
		
		for (int i=0; i < SHORTS_IN_PACK; i++)
			components[i] = (short) (( pack >>> (i*16) ) & 0xFFFF); 

		return components;
}

Any advice would be helpful, especially if worded as simply as possible.
Thank you :slight_smile:

1) Sort of. Java uses signed data types. A signed byte can represent the values of -128 to 127. An unsigned byte can represent the values of range 0 to 255.

Unsigned byte:

[tr][td]Decimal:[/td][td]+128[/td][td]+64[/td][td]+32[/td][td]+16[/td][td]+8[/td][td]+4[/td][td]+2[/td][td]+1[/td][/tr]
[tr][td]Binary:[/td][td]0[/td][td]0[/td][td]0[/td][td]0[/td][td]0[/td][td]0[/td][td]0[/td][td]0[/td][/tr]

Signed byte:

[tr][td]Decimal:[/td][td]-128[/td][td]+64[/td][td]+32[/td][td]+16[/td][td]+8[/td][td]+4[/td][td]+2[/td][td]+1[/td][/tr]
[tr][td]Binary:[/td][td]0[/td][td]0[/td][td]0[/td][td]0[/td][td]0[/td][td]0[/td][td]0[/td][td]0[/td][/tr]

Unsigned vs Signed:

[tr][td]Binary[/td][td]Signed[/td][td]Unsigned[/td][/tr]
[tr][td]0000 0000[/td][td]0[/td][td]0[/td][/tr]
[tr][td]0000 0010[/td][td]+2[/td][td]+2[/td][/tr]
[tr][td]0101 0010[/td][td]+82[/td][td]+82[/td][/tr]
[tr][td]1000 0000[/td][td]-128[/td][td]+128[/td][/tr]
[tr][td]1001 0000[/td][td]-112[/td][td]+144[/td][/tr]
[tr][td]0010 0100[/td][td]+36[/td][td]+36[/td][/tr]
[tr][td]1111 1111[/td][td]-1[/td][td]+255[/td][/tr]
[tr][td]1101 0110[/td][td]-42[/td][td]+214[/td][/tr]

2) Yes you can hold 4 shorts (16-bit integer) into one long (64-bit integer).

Issues with your code:

long pack = Long.MIN_VALUE;  // since Java primitive data types are signed, MIN_VALUE is -2^63. Instead initialize as 0
pack |= (components[i] & 0xFFFF) << (i*16);  // & 0xFFFF does absolutely nothing
components[i] = (short) (( pack >>> (i*16) ) & 0xFFFF); // better to use right shift (>>) operator and &0xFFFF will always result in 0

You’re storing the values like so:
short[3] short[2] short[1] short[0]

You’re shifting the bits so that;
pack >> 0 = short[3] short[2] short[1] short[0]
pack >> 16 = 0000… short[3] short[2] short[1]
pack >> 32 = 0000… 0000… short[3] short[2]
pack >> 48 = 0000… 0000… 0000… short[3]

So what you want to do is cast the rightmost bits to shorts. To do this, you need to do AND 0x000000000000FFFF. So pretty much,

components[i] = (short) (( pack >> (i*16) ) & 0x000000000000FFFF);