Странная целочисленная логика переполнения

Для следующего кода я получаю переполнение, но, к сожалению, я не могу понять, почему.

std::int8_t smallValue         = -1;
unsigned int value             = 500;
std::uint8_t anotherSmallValue = 1;

auto test = smallValue * value * anotherSmallValue;

потом test это довольно большое значение.

Может кто-нибудь объяснить, что здесь происходит?

1

Решение

Когда компилятор видит smallValue * value, он должен решить, каким будет тип данных результата, учитывая типы входных данных signed (8 бит) и unsigned int (обычно 16 или 32 бита). Правила C ++ гласят, что в этой ситуации результат будет без знака. Следовательно, значение smallValue * value не может быть -500, как вы ожидаете; вместо этого значение -500 интерпретируется как ПОЗИТИВНОЕ число.

Кроме того, здесь вы умножаете 8-битное значение на значение, которое обычно является 16- или 32-битным. Правила C ++ в этом сценарии гласят, что значение меньшего хранилища будет сначала приведено к тому же размеру, что и большее; так что в этом случае результат smallValue * value действительно будет достаточно большим, чтобы хранить несколько величин 500,

Продолжаем умножать на количество без знака anotherSmallValue (= 1) приводит к другому unsigned с тем же значением.

Потому что вы используете autoпоэтому тип возвращаемого значения будет unsigned,

Просто отбрасывая signed (например, путем определения значения test как int, а не auto, в свою очередь, обычно приводит результат всей операции обратно к signed значение, без изменения битов внутри; это будет правильно отображать -500, как вы ожидаете; однако, как отмечали другие авторы, в теории это довольно опасно, потому что технически это не гарантированно работает, хотя обычно это будет работать с современными компиляторами.

5

Другие решения

тип результата будет unsigned int проверить здесь. smallValue * value smallValue будет приведен к неподписанному, так что это выражение (unsigned)-1 * 500, Однако, если вы скомпилируете этот код с -Wsign-conversion — компилятор говорит вам, что вы делаете плохие вещи. ссылка на сайт

6

Вы получите тот же результат только с первыми двумя переменными (smallValue * value).

Во-первых, интегральные продвижения применяются к обоим значениям: int8_t становится int, а также unsigned int остается как есть.

Затем это правило из C ++ 11 5/9 применяется для определения типа результата:

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

Так -1 должен быть преобразован в unsigned int используя модульную арифметику, дающую большое положительное число. Умножение на 500 будет переполнено (снова с использованием модульной арифметики), давая другое большое число.

2

Превратите ‘auto’ в подписанный тип, и все будет в порядке:

long test = smallValue * value * anotherSmallValue;
1

Работа со смешанными типами всегда подвержена таким ошибкам. Я советую вам использовать один тип для арифметических операций

1
По вопросам рекламы [email protected]