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

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

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

auto test = smallValue * value * anotherSmallValue;

Впоследствии test — довольно большое значение.

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


person abergmeier    schedule 05.04.2013    source источник
comment
comment
Для меня урок здесь заключается в том, чтобы всегда устанавливать -Werror=sign-conversion   -  person abergmeier    schedule 05.04.2013


Ответы (5)


Когда компилятор видит 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, как вы ожидаете; однако, как отмечали другие авторы, это довольно опасно в теории, потому что технически это не гарантирует работу, хотя обычно это будет работать таким образом с сегодняшними компиляторами.

person Dan Nissenbaum    schedule 05.04.2013

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

person ForEveR    schedule 05.04.2013
comment
+1 за упоминание -Wsign-conversion. Я прочитал документацию gcc для него и был шокирован тем, что он не включен для -Wconversion. - person abergmeier; 05.04.2013

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

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

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

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

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

person Mike Seymour    schedule 05.04.2013

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

long test = smallValue * value * anotherSmallValue;
person metalhead    schedule 05.04.2013
comment
Это зависит от того, что вы подразумеваете под штрафом. Это даст тот же (большой положительный) результат, если long больше, чем unsigned int. Если он того же размера или вы измените его на int, то технически вы получите неопределенное поведение из-за подписанного переполнения; но на практике вы получите ожидаемый результат -500 на большинстве современных компьютеров. - person Mike Seymour; 05.04.2013

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

person fatihk    schedule 05.04.2013