Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Packing values into a single int

Tags:

java

Let's say I have a couple of variables like apple, orange, banana

I have 8 apples, 1 orange, 4 bananas.

Is it possible to somehow convert those values into a single integer and also revert back to their original values based on the computed integer value?

I found an example online.

int   age, gender, height;
short packed_info;
. . .
// packing
packed_info = (((age << 1) | gender) << 7) | height;
. . .
// unpacking
height = packed_info & 0x7f;
gender = (packed_info >>> 7) & 1;
age    = (packed_info >>> 8);

But it doesn't seem to work as it should when I entered random numbers.

like image 448
user303907 Avatar asked Dec 02 '22 04:12

user303907


1 Answers

How to do this

Yes, you can do this. The simple way to do this is what you are doing now, which is to allocate different bits of the integer to different values. Yours is something like this, for a 32-bit int:

|3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1     |      |             |
|1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 |  7   |6 5 4 3 2 1 0|
|                     Age                        |Gender|   Height    |

When you bitshift the value eight to the right, then you're taking just the age parts of the number. If you bitshift seven to the right and mask it with 1, i.e. value >>> 7 & 1, then you get the gender back. And if you just mask out the bottom seven bits (i.e. value & 0x7F), then you get the height.

Why it's fragile

Your example is missing something important: the range of the values. For example, the height value can never go higher than 127. If you try to store a height of 128 or higher, then the way you're writing it now will result in part of the height overwriting the gender, because you need eight bits to store a value that large. That's why random numbers don't work.

In the same way, if someone accidentally puts in a gender that isn't zero or one, then it'll mess up part of the age value, because you just can't store a number that high in a single bit.

The way to fix this in the assignment is by putting in the bit masks, like so:

packed_info = (((age << 1) | (gender & 1)) << 7) | (height & 0x7f);

When doing it this way, then you can be sure that gender won't overwrite age, and height won't overwrite the others. However, if you put in a height greater than 127, it'll be used mod 127.

Why you usually shouldn't

Because it's bug-prone and integers don't take very much memory. You can't just remember it's an int anymore, you need to remember what the bit layout looks like. It's easier to just keep three ints.

However, this sort of thing is still done when transmission speed matters. (For example, digital TV or other video, digital audio, the Ethernet protocols, etc.)

like image 139
jprete Avatar answered Dec 05 '22 10:12

jprete