4

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.

user303907
  • 543
  • 2
  • 9
  • 18

5 Answers5

13

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.)

jprete
  • 3,769
  • 21
  • 24
3

You can pack and unpack signed values. For example to pack two ints in one:

int pack2(int val1, int val2)
{
    int ret_val = (((val1 & 0xFFFF) << 16) | (val2 & 0xFFFF));
    return ret_val;
}

int[] unpack2(int packed)
{
    int val1 = ((packed >> 16) & 0xFFFF);
    // restore sign
    if ((val1 & 0x8000) != 0)
        val1 |= 0xFFFF0000;

    int val2 = (packed & 0xFFFF);
    // restore sign
    if ((val2 & 0x8000) != 0)
        val2 |= 0xFFFF0000;

    return new int[] { val1, val2 };
}

Of course You have to ensure that both values are between -0x7FFF and 0x7FFF (in case of two values).

liquid_code
  • 577
  • 7
  • 12
2

First: your idea is fine, you're shifting incorrectly (or you are in danger of doing so).

That said, it's more of a math question than a Java question, but let's play along :).

You can actually pack any number of integers into a single integer, assuming the number can grow indefinitely, with something like:

(Let n be the resulting number, n1...nk the numbers and Pn the n-th prime number)

n = 2^n1 + 3^n2 + 5^n3 ... Pn^nk

Now, that won't do, since unpacking is slow and you can't pack big numbers, or a big amount of them.

No matter what the technique, this problem you'll always have: the bigger your numbers, and the more there is of them, the harder it will be to pack them.

Where am I going with this? You can use bitwise packing, or any other sort you like, as long as there's actually space in an integer to hold your information. The bitwise logic you used will do just fine!

salezica
  • 74,081
  • 25
  • 105
  • 166
  • Hello, Thanks for you reply. How do I modify the solution above such that the maximum values that I can hold is up to 100? – user303907 Dec 29 '10 at 01:52
1

You can do it, though it depends on the size of the destination integer and the possible range of the "fruit" variables. If the range is larger, you require a different bitfield configuration. The number of bits required for a certain field is equal to log_2(max range value).

Reinderien
  • 11,755
  • 5
  • 49
  • 77
1

That kind of packing is a convention.

In your example

  • height is on 7 bits (from 0 to 127)
  • gender is on 1 bit (0 or 1)
  • age takes the remaining available bits (23 bits if signed, 24 if not, for an int in Java)

    <-age-><-gender-><-height->

If you use random numbers, you are likely to overflow the limited size of an entry (defined by convention). For instance if you set 128 for the height, you force the gender LSb (right bit) to be 1.

Déjà vu
  • 28,223
  • 6
  • 72
  • 100