Я делаю это для обработки как 24-разрядных целых чисел со знаком, так и без знака, просто добавляя / вычитая перед упаковкой и после распаковки, вот так:
void int24_write(uint8_t* bytes, int32_t val)
{
// Add to make the value positive.
val += (1 << 23);
// Make sure the value is in an acceptable range of
// [-2^23, +2^23).
assert(val >= 0 && val < (1 << 24));
// Pack the data from 32-bit to 24-bit.
bytes[0] = (uint8_t)(val & 0xff);
bytes[1] = (uint8_t)((val >> 8) & 0xff);
bytes[2] = (uint8_t)(val >> 16);
}
int32_t int24_read(const uint8_t* bytes)
{
// Unpack the data from 24-bit to 32-bit.
return (bytes[0] | (bytes[1] << 8) | (bytes[2] << 16)) -
(1 << 23);
}
Не уверен насчет оптимальной скорости, ноэто просто и без ветвей.Может упаковывать и распаковывать целые числа в диапазоне: [-2^23, +2^23)
.Если у кого-то есть предложения о том, как сделать это более эффективно, я был бы всем заинтересован.
Я склонен использовать его таким образом, чтобы данные для упаковки были достаточно маленькими, в среднем всего 3 или 4 целых числа(многие крошечные массивы сжимаются с 32-битных до 24-битных или менее), и обычно у нас есть только 3 или 4 целых числа для упаковки / распаковки за раз с, к сожалению, шаблоном произвольного доступа для извлечения этих небольших 24-битных массивов.Тем не менее, я имею тенденцию иметь несколько пакетов для распаковки / распаковки за раз, но опять же просто небольшое число, например 3 или 4, а не десятки, сотни или более.
Если вам нужен только неподписанный, то просто:
void uint24_write(uint8_t* bytes, uint32_t val)
{
// Make sure the value is in an acceptable range of
// [0, +2^24).
assert(val >= 0 && val < (1 << 24));
// Pack the data from 32-bit to 24-bit.
bytes[0] = (uint8_t)(val & 0xff);
bytes[1] = (uint8_t)((val >> 8) & 0xff);
bytes[2] = (uint8_t)(val >> 16);
}
uint32_t uint24_read(const uint8_t* bytes)
{
// Unpack the data from 24-bit to 32-bit.
return (bytes[0] | (bytes[1] << 8) | (bytes[2] << 16));
}
В этом случае целые числа без знака могут находиться в диапазоне [0, +2^24)
.