Приведение uint8_t к int8_t

Используя Microchip XC8, у меня есть конструкция

int16_t test;
uint8_t msb = 0xff;
uint8_t lsb = 0xf4;
uint8_t hyst = 0xff; 

test = ((((int16_t)msb)<<8) + (int16_t)lsb) + (int8_t)hyst);

Я ожидаю, что тест будет -13 (oxFFF3), так как я использовал числа со знаком, но он рассчитывается как 0xF3.

Почему?

Изменить: попробовал с gcc, и результат такой, как я ожидал.


person Frog    schedule 25.11.2015    source источник
comment
Стандартные целочисленные преобразования и неопределенное поведение.   -  person too honest for this site    schedule 25.11.2015
comment
Это поведение undefined, если int равно 16 битам из-за сдвига влево. Что такое sizeof(int)?   -  person interjay    schedule 25.11.2015
comment
Тогда это УБ. Если вы измените ((int16_t)msb) на ((uint16_t)msb), вы избавитесь от неопределенного поведения, хотя оно по-прежнему будет определяться реализацией.   -  person interjay    schedule 25.11.2015
comment
приведение к int16_t является избыточным и просто снижает читаемость, потому что эти значения все равно будут повышены до int.   -  person phuclv    schedule 25.11.2015


Ответы (2)


Как правильно заметил Олаф, операция сдвига может не иметь определенного поведения, если int имеет ширину 16, поскольку вы сдвигаете бит в знаковый бит int16_t. Тогда ваш код ошибочен, и вы не можете сделать вывод о ценности, которую может реализовать ваша платформа.

Если int больше, то с выражением все в порядке, вы суммируете только положительные значения, которые вписываются в int. Но результирующее значение затем будет преобразовано обратно в int16_t из-за инициализации, что приводит к преобразованию, определяемому реализацией, или повышению сигнала. Таким образом, вы должны проверить документацию вашего компилятора, что делает ваша платформа в этих случаях, и, вероятно, лучше избегать этого, если можете.

person Jens Gustedt    schedule 25.11.2015
comment
Он не определен только в том случае, если int равен 16 битам, поскольку целочисленное продвижение выполняется перед сдвигом. - person interjay; 25.11.2015
comment
@interjay, см. мою правку, в другом случае UB находится в преобразовании в int16_t. - person Jens Gustedt; 25.11.2015
comment
Если int больше, преобразование в int16_t будет определяться реализацией. - person interjay; 25.11.2015
comment
Если при преобразовании из int в int16_t происходит переполнение, поведение этого переполнения определяется реализацией (это может быть сигнал, определяемый реализацией, но он не очень часто встречается в современных архитектурах, даже встроенных). - person Pascal Cuoq; 25.11.2015
comment
По какой-то причине вам нравится использовать иносказания типа «не иметь определенного поведения» для незаконного использования знакового сдвига влево, но в стандарте это явно называется «неопределенное поведение». Как тот, кто настаивает на том, чтобы другие использовали mot juste (openwall.com/lists/ musl/2015/07/17/2 ), было бы проще просто назвать его так, как он называется в стандарте. - person Pascal Cuoq; 25.11.2015
comment
Сдвиг работает, как и ожидалось, и (((((int16_t)msb)‹‹8) + (int16_t)lsb)) оценивается правильно, но не с добавлением int8_t в конце. - person Frog; 25.11.2015
comment
@PascalCuoq, я делаю это не просто так. Основная проблема кода обычно заключается в том, что он ошибочен. УБ является лишь следствием этого, и я думаю, что этот термин слишком часто мистифицируется. Основная цель стандарта C — определить поведение кода, чтобы оно не служило оправданием для гнусавых демонов. - person Jens Gustedt; 25.11.2015
comment
Так как же это сделать? 16-битное значение извлекается в виде двух байтов из eeprom и добавляется к нему знаковый символ. Все беззнаковые символы при извлечении. - person Frog; 25.11.2015
comment
@Frog, это зависит от семантики, которую вы хотите иметь в своей операции. Результатом математической операции является значение, превышающее INT16_MAX. Что вы хотите, чтобы это было? - person Jens Gustedt; 25.11.2015
comment
Как я уже говорил выше, msb‹‹8 + lsb — это 16-битное целое число со знаком, а hyst — это char со знаком. Но, как это выглядит, если я использую промежуточный uint16_t, а затем привожу результат к int16-t, все работает. Я думаю, что gcc лучше справляется с этим стандартным способом или нет. - person Frog; 25.11.2015
comment
По какой причине мой комментарий был удален? - person too honest for this site; 25.11.2015
comment
@interjay: это для микроконтроллеров PIC16. Итак, int имеет 16 бит. - person too honest for this site; 25.11.2015

ОК из комментариев

int16_t test;
uint8_t msb = 0xff;
uint8_t lsb = 0xf4;
uint8_t hyst = 0xff; 

test = (int16_t)(((uint16_t)msb)<<8) + (uint16_t)lsb) + (int8_t)hyst;

решает проблему. Спасибо вам, ребята!

person Frog    schedule 25.11.2015
comment
В общем, вы не должны смешивать подписанные и неподписанные. Если вы собираете битовые поля, часто лучше использовать беззнаковые. - person too honest for this site; 25.11.2015