1

In this question, assume all integers are unsigned for simplicity.

Suppose I would like to write 2 functions, pack and unpack, which let you pack integers of smaller width into, say, a 64-bit integer. However, the location and width of the integers is given at runtime, so I can't use C bitfields.

Quickest is to explain with an example. For simplicity, I'll illustrate with 8-bit integers:

             * *
bit #    8 7 6 5 4 3 2 1
myint    0 1 1 0 0 0 1 1

Suppose I want to "unpack" at location 5, an integer of width 2. These are the two bits marked with an asterisk. The result of that operation should be 0b01. Similarly, If I unpack at location 2, of width 6, I would get 0b100011.

I can write the unpack function easily with a bitshift-left followed by a bitshift right.

But I can't think of a clear way to write an equivalent "pack" function, which will do the opposite.

Say given an integer 0b11, packing it into myint (from above) at location 5 and width 2 would yield

             * *
bit #    8 7 6 5 4 3 2 1
myint    0 1 1 1 0 0 1 1

Best I came up with involves a lot of concatinating bit-strings with OR, << and >>. Before I implement and test it, maybe somebody sees a clever quick solution?

Max
  • 3,384
  • 2
  • 27
  • 26

1 Answers1

5

Off the top of my head, untested.

int pack(int oldPackedInteger, int bitOffset, int bitCount, int value) {
    int mask = (1 << bitCount) -1;
    mask <<= bitOffset;
    oldPackedInteger &= ~mask;
    oldPackedInteger |= value << bitOffset;
    return oldPackedInteger;
}

In your example:

int value = 0x63;
value = pack(value, 4, 2, 0x3);

To write the value "3" at an offset of 4 (with two bits available) when 0x63 is the current value.

EboMike
  • 76,846
  • 14
  • 164
  • 167
  • 1
    Yep. And similarly for the unpack method: The OP doesn't need to use "a bitshift-left followed by a bitshift right" to unpack; a right-shift followed by an `&` would do the trick. – LukeH Mar 01 '11 at 01:46
  • @Luke: I don't quite get the example though. How does the unpack result in 0x0b01 and 0xb100011? – EboMike Mar 01 '11 at 01:49
  • @EboMike, bit 5 is 0, bit 6 is 1. – Max Mar 01 '11 at 01:51
  • @EboMike, tested it and your solution works -- thanks! Nice trick with (1 << bitCount) - 1. – Max Mar 01 '11 at 01:58
  • @EboMike: And to answer comment regarding the premise, for posterity's sake. A normal bitfield is what I was looking for. I.e. "packing" an integer into one place shouldn't change the other stuff within the integer. – Max Mar 01 '11 at 02:03
  • @Max: Glad to help! Bit magic is awesome, the possibilities are endless. – EboMike Mar 01 '11 at 02:09
  • will fails if bitcount is 32 – Parvinder Singh May 03 '14 at 06:10
  • Use `unsigned` integers for this kind of things always, so that you prevent UB from overflow. Also, I would recommend fixed width ones. – alx - recommends codidact Mar 02 '19 at 16:38