Int promotion: Являются ли следующие четко определенные?

Предположим, что в реализации C (например, в компиляторе x86 C) USHRT_MAX = 65535 и INT_MAX = 2147483647. Является ли тогда следующее утверждение корректным?

unsigned short product = USHRT_MAX * USHRT_MAX;

В соответствии со следующим стандартом C99 оба операнда продвигаются до int (так как int может представлять все возможные значения unsigned short), и поэтому результат не является корректным, так как переполнение будет (65535 ^ 2 = 4294836225 > 2147483647), что означает, что значение product не определено:

6.3.1.1-1

Если int может представлять все значения исходного типа, это значение равно преобразован в int; в противном случае он преобразуется в unsigned int. Они называются целыми рекламными акциями. (48) Все остальные типы без изменений целыми рекламными акциями.

48) Целые поощрения применяются только: как часть обычного арифметические преобразования, к некоторым выражениям аргументов, к операндов унарных операторов +, - и ~ и обоих операндов операторы сдвига, как указано в их соответствующих подпунктах.

Однако, согласно следующему, результат корректно определен, поскольку вычисления с использованием неподписанных операндов не переполняются:

6.2.5-9

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

Имеет ли переменная product в вышеупомянутом выражении определенное значение?

EDIT: что должно произойти в следующем случае?

unsigned short lhs = USHRT_MAX;
unsigned short rhs = USHRT_MAX;
unsigned short product = lhs * rhs;

Ответ 1

Победа продвигается.

Говорит раздел 5.2.4.2.1 о константах USHRT_MAX и т.д.:

Значения, приведенные ниже, должны быть заменены постоянными выражениями, подходящими для использования в директивах #if предварительной обработки. Кроме того, кроме CHAR_BIT и MB_LEN_MAX, следующий должен быть заменен выражениями, которые имеют тот же тип, что и выражение, являющееся объектом соответствующего типа, преобразованного в соответствии с целыми рекламными акциями.

Таким образом, умножение находится на int s и не содержит беззнаковых операндов, недвусмысленно, не существует соответствующего способа реализации USHRT_MAX для получения операции с неподписанными операндами, если USHRT_MAX < INT_MAX. Таким образом, у вас есть переполнение и поведение undefined.

Относительно добавленного вопроса

EDIT: что должно произойти в следующем случае?

unsigned short lhs = USHRT_MAX;
unsigned short rhs = USHRT_MAX;
unsigned short product = lhs * rhs;

Это точно такая же ситуация. Операнды * подвергаются целочисленным акциям, все значения типа unsigned short могут быть представлены как int по предположению о значениях USHRT_MAX и INT_MAX, поэтому умножение на int s и с указанными значениями переполнения.

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

Ответ 2

Вы получаете UB, поскольку к тому времени, когда применяется оператор умножения, его операнды уже являются целыми числами (из-за того, что в начале появляются акции до int).

Вы можете обходиться с этим:

unsigned short product = USHRT_MAX * (unsigned)USHRT_MAX;

Доказательство того, что (unsigned)some_integer остается без знака:

#include <stdio.h>

int main(void)
{
  printf("1u * (-1) = %f\n", (((unsigned)1) * (-1)) + 0.0);
  printf("1 * (-1) = %f\n", (1 * (-1)) + 0.0);
  return 0;
}

Выход (ideone):

1u * (-1) = 4294967295.000000
1 * (-1) = -1.000000

Хороший улов, кстати.