# Q：一个字节中的两个值

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

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

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.

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

（如果你能失去精度-你没有告诉-你可以，例如存储只有最高的位…）

``````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; }
``````

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

}

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.

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

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.

``````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;
}
``````

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

``````uint8_t var; /* range 0-255 */

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

``````uint8_t var; /* range 0-255 */

data1 = (var & 0x0F);      /* range 0-15 */
data2 = (var & 0xF0) >> 4; /* range 0-15 */
``````
c++  c  hex  bit-manipulation