В дополнении к восьмибитному двоичному знаковый бит можно интерпретировать как имеющий значение места -2 8 , что, конечно, -256.Это на самом деле именно так, как его характеризует стандарт C.Поэтому, учитывая 8-битное значение, хранящееся в uint8_t
, которое вы хотите интерпретировать как целое число дополнения до двух, это арифметический способ сделать это:
uint8_t u8 = /* ... */;
int8_t i8 = (u8 & 0x7f) - (u8 > 0x7f) * 0x100;
Обратите внимание, что вся арифметикавыполняется сначала повышением операндов до (подписи) int
, поэтому нет переполнения (потому что диапазон int
достаточно велик для этого), а также арифметического обхода без знака.Арифметический результат гарантированно находится в диапазоне int8_t
, поэтому нет риска переполнения при преобразовании результата в этот тип.
Вы заметите сходство между этим вычислением и вашим,но этот позволяет избежать троичного оператора, используя результат реляционного выражения u8 > 0x7f
(0 или 1) непосредственно в арифметике, тем самым избегая любого ветвления, и он обходится без ненужных приведений.(Ваши также не нуждаются в приведениях.)
Обратите также внимание, что если вы столкнетесь с какой-то странной реализацией, которая не обеспечивает int8_t
(потому что ее char
шире, чем 8 бит, или ееsigned char
s не используют дополнения до двух), тогда этот арифметический подход все еще работает в смысле вычисления правильного значения, и вы можете быть уверены в безопасной записи этого значения в int
или short
.Таким образом, абсолютно наиболее переносимым способом извлечения значения из интерпретации uint8_t для 8-битного двоичного числа будет
uint8_t u8 = /* ... */;
int i8 = (u8 & 0x7f) - (u8 > 0x7f) * 0x100;
В качестве альтернативы, если вы готовы положиться на int8_t
быть типом символа - т.е. псевдонимом для char
или signed char
- тогда совершенно стандартно делать эту работу следующим образом:
uint8_t u8 = /* ... */;
int8_t i8 = *(int8_t *)&u8;
Это одинЕще более вероятно, что компилятор будет оптимизирован, чем альтернатива memcpy()
, представленная в другом ответе, но, в отличие от альтернативы memcpy
, эта формально имеет неопределенное поведение, если int8_t
оказывается не длябыть типом персонажа.С другой стороны, и этот, и memcpy()
подход зависят от реализации для предоставления типа int8_t
, и даже более маловероятно, чем реализация, не обеспечивающая int8_t
, состоит в том, что реализация предоставляет int8_t
, который не может бытьтип символа.