Недавно мы обнаружили странное поведение в каком-то старом коде. Этот код работал много лет, но сломался на некоторых платформах (XBox 360, PowerPC), когда оптимизация компилятора была включена макс. Обычно я подозреваю неопределенное поведение.
Код выглядит примерно так:
#include <stdint.h>
uint32_t sign_extend16(uint32_t val)
{
return (int32_t)(int16_t)val;
}
Это часть эмулятора, поэтому рассматриваемая операция не должна быть слишком странной. Обычно я ожидаю, что это будет учитывать только младшие 16 бит и расширять их знаком до 32 бит. Видимо, так он вел себя веками. На x86_64 GCC дает мне следующий результат:
0000000000000000 <sign_extend16>:
0: 0f bf c7 movswl %di,%eax
3: c3 retq
Однако из того, что я мог понять о стандарте, преобразование беззнакового в знаковое не определено, если невозможно представить значение беззнакового с типом со знаком.
Может ли компилятор предположить, что значение без знака должно быть в диапазоне [0, 32767]
, поскольку любое другое значение будет неопределенным? В этом случае приведение к int16_t
и еще одно приведение к int32_t
ничего не даст. В этом случае разрешено ли компилятору преобразовать код в простой ход?