一个字节中的两个值

Two values in one byte

本文关键字:两个 字节 一个      更新时间:2023-10-16

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

你的问题的答案是NO。可以将一个字节拆分为两个数字,但是这两个数字的位数之和必须为<= 8。因为范围0-127需要7位,字节中的其他数字只能是1位,即0-1。

由于基数的原因,不能将两个小整数存储在0…127的范围是0…255年范围内。换句话说,笛卡尔积[0;127]×[0;127]有214个元素,且大于28([0;255]间隔的基数,以字节为单位)

(如果你可以承受精度的损失-你没有告诉-你可以,例如,通过只存储最高位…)

也许你的问题是:我可以存储两个小整数从[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中使用位字段。然后(最重要的是)以这种方式处理小块可能比使用字节更低效,代码可读性更差。

和更新单个nibble,例如使用

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

}

有时是昂贵的(它涉及内存负载和内存存储,因此使用CPU缓存和缓存一致性机制),最重要的是通常是非原子操作(如果两个不同的线程同时在相同的地址p上调用update_left_nibble会发生什么-即指针混叠-是未定义的行为)。

作为一个经验法则,避免在一个字节中打包超过一个数据项,除非你确定它是值得的(例如,你有十亿这样的数据项)。

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

您可以使用C和c++的位域语法声明具有位打包存储的变量:

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

在这个例子中,sizeof(packed_values)应该等于2,因为只使用了16位,尽管有三个字段。

这比对<<&操作符使用按位算术更简单,但它仍然与普通变量不完全相同:位字段没有地址,因此不能有指向地址的指针(或c++引用)。

我可以使用一个字节来存储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年,实际上)。你只是失去了一些精度,类似于浮点类型。

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

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

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