pistacchio
pistacchio

Reputation: 58873

Two values in one byte

In a single nibble (0-F) I can store one number from 0 to 15. In one byte, I can store a single number from 0 to 255 (00 - FF). Can I use a byte (00-FF) to store two different numbers each in the range 0-127 (00 - 7F)?

Upvotes: 3

Views: 3021

Answers (5)

For obvious cardinality reasons, you cannot store two small integers in the 0 ... 127 range in one byte of 0 ... 255 range. In other words the cartesian product [0;127]×[0;127] has 214 elements which is bigger than 28 (the cardinal of the [0;255] interval, for bytes)

(If you can afford losing precision - which you didn't tell - you could, e.g. by storing only the highest bits ...)

Perhaps your question is: could I store two small integers from [0;15] in a byte? Then of course you could:

typedef unsigned unibble_t; // unsigned nibble in [0;15]
uint8_t make_from_two_nibbles(unibble_t l, unibble_t r) {
  assert(l<=15);
  assert(r<=15);
  return (l<<4) | r;
}

unibble_t left_nible (uint8_t x) { return x >> 4; }
unibble_t right_nibble (uint8_t) { return x & 0xf; }

But I don't think you always should do that. First, you might use bit fields in struct. Then (and most importantly) dealing with nibbles that way might be more inefficient and make less readable code than using bytes.

And updating a single nibble, e.g. with

void update_left_nibble (uint8_t*p, unibble_t l) {
  assert (p);
  assert (l<=15);
  *p = ((l<<4) | ((*p) & 0xf));

}

is sometimes expensive (it involves a memory load and a memory store, so uses the CPU cache and cache coherence machinery), and most importantly is generally a non-atomic operation (what would happen if two different threads are calling simultaneously update_left_nibble on the same address p -i.e. with pointer aliasing- is undefined behavior).

As a rule of thumb, avoid packing more than one data item in a byte unless you are sure it is worthwhile (e.g. you have a billion of such data items).

Upvotes: 5

user4780495
user4780495

Reputation: 2912

You can store two data in range 0-15 in a single byte, but you should not (one var = one data is a better design).

If you must, you can use bit-masks and bit-shifts to access to the two data in your variable.

uint8_t var; /* range 0-255 */

data1 = (var & 0x0F);      /* range 0-15 */
data2 = (var & 0xF0) >> 4; /* range 0-15 */

Upvotes: -1

Nikolai Ruhe
Nikolai Ruhe

Reputation: 81868

Can I use a byte to store two numbers in the range 0-127?

Of course you can:

uint8_t storeTwoNumbers(unsigned a, unsigned b) {
    return ((a >> 4) & 0x0f) | (b & 0xf0);
}

uint8_t retrieveTwoNumbers(uint8_t byte, unsigned *a, unsigned *b) {
    *b = byte & 0xf0;
    *a = (byte & 0x0f) << 4;
}

Numbers are still in range 0...127 (0...255, actually). You just loose some precision, similar to floating point types. Their values increment in steps of 16.

Upvotes: 1

Potatoswatter
Potatoswatter

Reputation: 137810

One byte is not enough for two values in 0…127, because each of those values needs log2(128) = 7 bits, for a total of 14, but a byte is only 8 bits.

You can declare variables with bit-packed storage using the C and C++ bitfield syntax:

struct packed_values {
    uint8_t first : 7;
    uint8_t second : 7;
    uint8_t third : 2;
};

In this example, sizeof(packed_values) should equal 2 because only 16 bits were used, despite having three fields.

This is simpler than using bitwise arithmetic with << and & operators, but it's still not quite the same as ordinary variables: bit-fields have no addresses, so you can't have a pointer (or C++ reference) to one.

Upvotes: 1

user3386109
user3386109

Reputation: 34829

The answer to your question is NO. You can split a single byte into two numbers, but the sum of the bits in the two numbers must be <= 8. Since, the range 0-127 requires 7 bits, the other number in the byte can only be 1 bit, i.e. 0-1.

Upvotes: 10

Related Questions