Data Compression
Byte Encoding
Numerical Representation
Bit Manipulation
Memory Optimization

Compress two or more numbers into one byte

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In computer science, compressing multiple numbers into a single byte is a practical technique for embedded systems, network protocols, game development, and any context where memory is scarce. A byte consists of 8 bits, giving 28=2562^8 = 256 possible values. By dividing those 8 bits into separate fields, each field can represent a different number. This article explains the technique of bit packing, walks through examples with packing and unpacking, and covers the tradeoffs involved.

How Bit Packing Works

Bit packing divides a byte into non-overlapping fields. Each field is a contiguous group of bits dedicated to one number. The total bits allocated across all fields must not exceed 8.

The range of values a field can hold depends on its bit width:

BitsRangeNumber of Values
100 to 112
200 to 334
300 to 778
400 to 151516
500 to 313132
600 to 636364

In general, nn bits can represent values from 00 to 2n12^n - 1.

Example: Packing Two Numbers

Suppose you need to store two numbers:

  • Number A: range 0 to 3 (requires 2 bits)
  • Number B: range 0 to 15 (requires 4 bits)

Total: 6 bits used, 2 bits unused.

Packing Layout

 
Bit:  7  6  5  4  3  2  1  0
      [  A  ] [    B    ] [--]

The high 2 bits hold A, the next 4 bits hold B, and the lowest 2 bits are unused (or available for a third field).

Packing Code

To pack A and B into a single byte:

packed=(A6)    (B2)\text{packed} = (A \ll 6) \;|\; (B \ll 2)

For example, if A = 2 (binary 10) and B = 9 (binary 1001):

packed=(106)    (10012)=10000000    00100100=10100100\text{packed} = (10 \ll 6) \;|\; (1001 \ll 2) = 10000000 \;|\; 00100100 = 10100100

The packed byte is 0xA4 (decimal 164).

Unpacking Code

To extract the numbers back:

A=(packed6)  &  0x03A = (\text{packed} \gg 6) \;\&\; 0\text{x}03 B=(packed2)  &  0x0FB = (\text{packed} \gg 2) \;\&\; 0\text{x}0F

python
1def pack(a: int, b: int) -> int:
2    return (a << 6) | (b << 2)
3
4def unpack(byte: int) -> tuple[int, int]:
5    a = (byte >> 6) & 0x03
6    b = (byte >> 2) & 0x0F
7    return a, b
8
9packed = pack(2, 9)
10print(f"Packed: {packed:#010b} ({packed})")  # 0b10100100 (164)
11print(f"Unpacked: {unpack(packed)}")          # (2, 9)

Example: Packing Three Numbers

You can fit three numbers if their total bit widths do not exceed 8:

  • X: range 0 to 7 (3 bits)
  • Y: range 0 to 3 (2 bits)
  • Z: range 0 to 7 (3 bits)

Layout:

 
Bit:  7  6  5  4  3  2  1  0
      [   X   ] [ Y ] [   Z   ]

Packing: packed=(X5)    (Y3)    Z\text{packed} = (X \ll 5) \;|\; (Y \ll 3) \;|\; Z

Unpacking:

  • X=(packed5)  &  0x07X = (\text{packed} \gg 5) \;\&\; 0\text{x}07
  • Y=(packed3)  &  0x03Y = (\text{packed} \gg 3) \;\&\; 0\text{x}03
  • Z=packed  &  0x07Z = \text{packed} \;\&\; 0\text{x}07

Summary Table

FieldBitsRangeMaskExtract
A200 to 330x03(byte6)  &  0x03(\text{byte} \gg 6) \;\&\; 0\text{x}03
B400 to 15150x0F(byte2)  &  0x0F(\text{byte} \gg 2) \;\&\; 0\text{x}0F
Unused2---

Considerations and Tradeoffs

Overflow Risk

If a value exceeds the allocated bits, the extra high bits are silently lost. For example, storing the value 5 (binary 101) in a 2-bit field produces 01 (value 1). Always validate inputs before packing.

No Precision Loss for Integers

Unlike floating-point compression, integer bit packing is lossless within the declared range. The value you pack is exactly the value you unpack.

Increased Code Complexity

Bit manipulation adds complexity to your code. Every pack and unpack site needs to agree on the field layout. Consider defining constants or using a struct/bitfield abstraction in your language.

Endianness

Bit packing within a single byte is endianness-independent. However, if you pack across multiple bytes, byte order matters and must be handled consistently.

Applications

  • Embedded Systems: Configuration registers on microcontrollers often pack multiple flags and small values into single bytes.
  • Network Protocols: Protocol headers (TCP flags, DNS record types) use bit-level packing to minimize packet size.
  • Game Development: Storing multiple character attributes (health tier, weapon type, status flags) in limited memory.
  • File Formats: Image formats like BMP store pixel data with specific bit depths per channel.

Conclusion

Compressing multiple numbers into one byte through bit packing is a straightforward technique that uses shift and mask operations. The key constraint is that the total bits across all fields must not exceed 8. Within that limit, the technique is lossless and efficient. The main costs are code complexity and the need for consistent field layout definitions across pack and unpack sites.


Course illustration
Course illustration

All Rights Reserved.