Для следующего кода я получаю переполнение, но, к сожалению, я не могу понять, почему.
std::int8_t smallValue = -1;
unsigned int value = 500;
std::uint8_t anotherSmallValue = 1;
auto test = smallValue * value * anotherSmallValue;
потом test
это довольно большое значение.
Может кто-нибудь объяснить, что здесь происходит?
Когда компилятор видит 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
, как вы ожидаете; однако, как отмечали другие авторы, в теории это довольно опасно, потому что технически это не гарантированно работает, хотя обычно это будет работать с современными компиляторами.
тип результата будет unsigned int
проверить здесь. smallValue * value
smallValue
будет приведен к неподписанному, так что это выражение (unsigned)-1 * 500
, Однако, если вы скомпилируете этот код с -Wsign-conversion
— компилятор говорит вам, что вы делаете плохие вещи. ссылка на сайт
Вы получите тот же результат только с первыми двумя переменными (smallValue * value
).
Во-первых, интегральные продвижения применяются к обоим значениям: int8_t
становится int
, а также unsigned int
остается как есть.
Затем это правило из C ++ 11 5/9 применяется для определения типа результата:
В противном случае, если операнд с целым типом без знака имеет ранг больше или равен
ранг типа другого операнда, операнд со знаком целого типа должен быть преобразован в
тип операнда с целым типом без знака.
Так -1
должен быть преобразован в unsigned int
используя модульную арифметику, дающую большое положительное число. Умножение на 500 будет переполнено (снова с использованием модульной арифметики), давая другое большое число.
Превратите ‘auto’ в подписанный тип, и все будет в порядке:
long test = smallValue * value * anotherSmallValue;
Работа со смешанными типами всегда подвержена таким ошибкам. Я советую вам использовать один тип для арифметических операций