Подтвердить что ты не робот

32-разрядное беззнаковое умножается на 64-битное поведение undefined?

Итак, у меня есть этот код:

uint32_t s1 = 0xFFFFFFFFU;
uint32_t s2 = 0xFFFFFFFFU;
uint32_t v;
...
v = s1 * s2; /* Only need the low 32 bits of the result */

Во всех последующих случаях я предполагаю, что компилятор не может иметь никаких предубеждений в диапазоне s1 или s2, причем инициализаторы служат только для примера выше.

Если я скомпилировал это в компиляторе с целым размером 32 бита (например, при компиляции для x86), не проблема. Компилятор просто использовал бы s1 и s2 как uint32_t типизированные значения (не будучи в состоянии продвигать их дальше), а умножение просто даст результат, как говорится в комментарии (по модулю UINT_MAX + 1, который равен 0x100000000 в этом случае).

Однако, если я скомпилировал это в компиляторе с целым размером 64 бит (например, для x86-64), может быть поведение undefined из того, что я могу вывести из стандарта C. Целочисленное продвижение показало бы, что uint32_t может быть увеличено до int (64-битная подпись), умножение затем попытается умножить два int 's, которые, если они имеют значения, показанные в примере, целочисленное переполнение, которое представляет собой поведение undefined.

Я исправляю это, и если да, то как бы вы избегали его разумным способом?

Я заметил этот вопрос, который похож, но охватывает С++: Каков наилучший способ С++ для непрерывного без целого числа целых чисел без знака?. Здесь я хотел бы получить ответ, применимый к C (предпочтительно совместимый с C89). Я бы не подумал о том, чтобы сделать плохую 32-битную машину, потенциально выполняющую 64-битное умножение приемлемого ответа (обычно в коде, где это было бы опасно, 32-разрядная производительность может быть более критичной, как правило, это медленные машины).

Обратите внимание, что одна и та же проблема может применяться к 16-битным беззнаковым ints при компиляции с компилятором, имеющим 32-битный размер int или unsigned chars при компиляции с компилятором, имеющим размер 16 бит int (последний может быть общим с компиляторами для 8-битные ЦП: стандарт C требует, чтобы целые числа составляли не менее 16 бит, поэтому вероятно, что соответствующий компилятор затронут).

4b9b3361

Ответ 1

Самый простой способ получить умножение в неподписанном типе, который не менее uint32_t, а также не менее unsigned int, должен включать выражение типа unsigned int.

v = 1U * s1 * s2;

Это либо преобразует 1U в uint32_t, либо s1 и s2 в unsigned int, в зависимости от того, что подходит для вашей конкретной платформы.

@Deduplicator комментирует, что некоторые компиляторы, где uint32_t уже, чем unsigned int, могут предупреждать о неявном преобразовании в присваивании и отмечают, что такие предупреждения, вероятно, подавляются, делая явное преобразование:

v = (uint32_t) (1U * s1 * S2);

Он выглядит немного менее изящным, на мой взгляд, тем не менее.

Ответ 2

Поздравляем с поиском точки трения.

Возможный путь:

v = (uint32_t) (UINT_MAX<=0xffffffff
  ? s1 * s2
  : (unsigned)s1 * (unsigned)s2);

В любом случае, похоже, что добавление некоторых typedefs в <stdint.h> для типов, гарантированных быть не меньше, чем int будет в порядке; -).