C - три байта в один со знаком int

У меня есть датчик, который дает свой выход в три байта. Я читаю это так:

unsigned char byte0,byte1,byte2;

byte0=readRegister(0x25);
byte1=readRegister(0x26);
byte2=readRegister(0x27);

Теперь я хочу, чтобы эти три байта были объединены в одно число:

int value;
value=byte0 + (byte1 << 8) + (byte2 << 16);

это дает мне значения от 0 до 16,777,215, но я ожидаю значения от -8,388,608 до 8,388,607. Я думал что int был уже подписан его реализацией. Даже если я попытаюсь определить это как signed int value; это все еще дает мне только положительные числа. Итак, я думаю, мой вопрос, как преобразовать int в дополнение к двум?

Спасибо!

2 ответа

Решение

То, что вам нужно выполнить, называется расширением знака. У вас есть 24 значащих бита, но вы хотите 32 значащих бита (обратите внимание, что вы предполагаете int быть 32-битным, что не всегда верно; вам лучше использовать тип int32_t определяется в stdint.h). Пропущенные 8 старших битов должны быть либо всеми нулями для положительных значений, либо всеми единицами для отрицательных. Он определяется старшим значащим битом 24-битного значения.

int32_t value;
uint8_t extension = byte2 & 0x80 ? 0xff:00; /* checks bit 7 */
value = (int32_t)byte0 | ((int32_t)byte1 << 8) | ((int32_t)byte2 << 16) | ((int32_t)extension << 24);

РЕДАКТИРОВАТЬ: Обратите внимание, что вы не можете сдвинуть 8-битное значение на 8 или более бит, это неопределенное поведение. Сначала вам придется привести его к более широкому типу.

#include <stdint.h>
uint8_t byte0,byte1,byte2;
int32_t answer;

// assuming reg 0x25 is the signed MSB of the number 
// but you need to read unsigned for some reason
byte0=readRegister(0x25);
byte1=readRegister(0x26);
byte2=readRegister(0x27);

// so the trick is you need to get the byte to sign extend to 32 bits
// so force it signed then cast it up
answer = (int32_t)((int8_t)byte0); // this should sign extend the number
answer <<= 8;
answer |= (int32_t)byte1; // this should just make 8 bit field, not extended
answer <<= 8;
answer |= (int32_t)byte2;

Это также должно работать

answer = (((int32_t)((int8_t)byte0))<<16) + (((int32_t)byte1)<< 8) + byte2;

Я могу быть слишком агрессивен с круглыми скобками, но я никогда не доверяю себе операторам смещения:)

Другие вопросы по тегам