Почему static_cast на выражении действует дистрибутивно?

Мне нужно взять 2 unsigned 8-битных значения и вычесть их, а затем добавить это значение в 32-разрядный аккумулятор. 8-битовое вычитание может быть опущено, и это нормально (unsigned int underflow определяется поведением, поэтому проблем нет).

Я ожидал бы, что static_cast<uint32_t>(foo - bar) должен делать то, что я хочу (где foo и bar - оба uint8_t). Но, казалось бы, это отбрасывает их сначала, а затем выполняет 32-битное вычитание, тогда как мне нужно, чтобы он был переполнен как 8-битная переменная. Я знаю, что могу просто поменять 256, но я пытаюсь понять, почему он работает таким образом.

Пример здесь: https://ideone.com/TwOmTO

uint8_t foo = 5;
uint8_t bar = 250;

uint8_t diff8bit = foo - bar;
uint32_t diff1 = static_cast<uint32_t>(diff8bit);

uint32_t diff2 = static_cast<uint32_t>(foo) - static_cast<uint32_t>(bar);

uint32_t diff3 = static_cast<uint32_t>(foo - bar);

printf("diff1 = %u\n", diff1);
printf("diff2 = %u\n", diff2);
printf("diff3 = %u\n", diff3);

Вывод:

diff1 = 11
diff2 = 4294967051
diff3 = 4294967051

Я бы предположил, что diff3 будет иметь такое же поведение, как diff1, но на самом деле это то же самое, что и diff2.

Так почему это происходит? Насколько я могу судить, компилятор должен вычитать два 8-битных значения, а затем кастинг на 32-битный, но это явно не так. Это связано с тем, как static_cast ведет себя в выражении?

Ответ 1

Для большинства арифметических операторов (включая -) операнды претерпевают обычные арифметические преобразования. Одно из этих преобразований состоит в том, что любое значение типа, более узкого, чем int, увеличивается до int. (Стандартная ссылка: [expr]/10).

Таким образом, выражение foo - bar становится (int)foo - (int)bar, давая (int)-245. Затем вы добавите это значение в uint32_t, которое даст большое положительное число.

Чтобы получить результат, который вы намереваетесь, введите uint8_t вместо uint32_t. В качестве альтернативы, используйте оператор модуля % для результата приведения в uint32_t.

Невозможно выполнить вычисления непосредственно при более узкой точности, чем int

Ответ 2

Проблема заключается не в static_cast, но в вычитании операнды аддитивных операторов имеют обычные арифметические преобразования, применяемые к ним, и в в этом случае интегральные акции, которые приводят к тому, что оба операнда вычитания продвигаются до int:

static_cast<uint32_t>(foo - bar);
                      ^^^   ^^^

С другой стороны:

static_cast<uint8_t>(foo - bar);

создаст желаемый результат.

из проекта стандартного раздела С++ 5.7 [expr.add] говорит:

Аддитивные операторы + и - группа слева направо. Обычные арифметические преобразования выполняются для операнды арифметики или типа перечисления.

это приводит к интегральным акциям, раздел 5 [expr] говорит:

В противном случае интегральные акции (4.5) должны выполняться на обоих операндах

в результате которого оба операнда преобразуются в int, раздел 4.5 [conv.prom] говорит:

Значение целочисленного типа, отличного от bool, char16_t, char32_t или wchar_t, целочисленное преобразование rank (4.13) меньше ранга int может быть преобразован в prvalue типа int, если int может представлять все значения типа источника; в противном случае исходное значение prvalue может быть преобразовано в prvalue типа unsigned внутр.

а затем применяется static_cast to uint32_t, что приводит к преобразованию, которое определяется в разделе 4.7 [conv.integral]:

Если тип назначения не указан, результирующее значение представляет собой наименьшее целое число без знака, сравнимое с источником integer (по модулю 2n, где n - количество бит, используемых для представления неподписанного типа). [

Вопросы Почему короткое преобразование в int перед арифметическими операциями в C и С++? объясняет, почему для арифметических операций рекламируются типы, меньшие, чем int.