一个字节中的两个值

Two values in one byte

在一个半字节 (0-F) 中,我可以存储一个从 0 到 15 的数字。在一个字节中,我可以存储一个从 0 到 255 (00 - FF) 的数字。 我可以使用一个字节 (00-FF) 来存储两个不同的数字,每个数字都在 0-127 (00 - 7F) 范围内吗?

你的问题的答案是否定的。您可以将单个字节拆分为两个数字,但是两个数字中的位之和必须 <= 8。因为,范围 0-127 需要 7 位,另一个数字in byte只能是1位,即0-1.

显然 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] 有 214 个元素大于 28([0;255]区间的基数,for bytes)

(如果你能承受失去精度的后果 - 你没有告诉 - 你可以,例如只存储最高位...)

也许你的问题是:我可以在一个字节中存储来自 [0;15] 的两个小整数吗?那么你当然可以:

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

但我认为您不应该总是那样做。首先,您可以在 struct 中使用位域。然后(也是最重要的)以这种方式处理半字节可能比使用字节更低效并且代码可读性更差。

并更新单个半字节,例如与

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

}

有时很昂贵(它涉及内存加载和内存存储,因此使用 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)。

根据经验,避免在一个字节中打包多个数据项,除非您确定这样做是值得的(例如,您有十亿个此类数据项)。

您可以在单个字节中存储两个数据在 0-15 范围内,但您不应该这样做(一个 var = 一个数据是更好的设计)。

如果必须,您可以使用位掩码和位移来访问变量中的两个数据。

uint8_t var; /* range 0-255 */

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

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

当然可以:

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

数字仍在 0...127 范围内(实际上是 0...255)。您只是失去了一些精度,类似于浮点类型。它们的值以 16 为步长递增。

一个字节对于 0…127 中的两个值是不够的,因为每个值都需要 log2(128) = 7 位,总共 14 个,但是一个字节只有8位。

您可以使用 C 和 C++ bitfield 语法声明具有位压缩存储的变量:

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

在此示例中,sizeof(packed_values) 应等于 2,因为尽管具有三个字段,但仅使用了 16 位。

这比使用 <<& 运算符的按位运算更简单,但它仍然与普通变量不完全相同:位域没有地址,所以你不能指向一个的指针(或 C++ 引用)。