Все предыдущие ответы указывали на то, что результатом является UB (неопределенное поведение), поскольку 2147483648
не является допустимым значением int32_t
.И все мы знаем, что UB означает, что может случиться что угодно, включая выпадение демонов из носа.Вопрос в том, почему поведение cout
выводит отрицательное значение, которое, по-видимому, является худшим значением, которое оно могло бы выбрать случайным образом?
Я попытаюсь обосновать его в системе дополнения до двух.Отрицание на процессоре - довольно сложная операция.Вы не можете сделать это за один шаг.Один из способов реализации отрицания, т. Е. int32_t positivenum = -num
, состоит в том, чтобы выполнить инверсию битов с последующим добавлением 1, т. Е. int32_t positivenum = ~num + 1
, где ~
- оператор побитового отрицания, а +1
- для исправления ошибки выключения на единицу.,Например, отрицание 0x00000000
равно 0xFFFFFFFF + 1
, что составляет 0x00000000
(после пролонгации, что делает большинство процессоров).Вы можете убедиться, что это работает для большинства целых чисел ... за исключением 2147483648. 2147483648 хранится как 0x80000000 в дополнении к двум.Когда вы инвертируете и добавляете один, вы получаете
- (min) = -(0x80000000)
= ~(0x80000000) + 1
= 0x7FFFFFFF + 1
= 0x80000000
= min
Так волшебно, унарный оператор -
, работающий на min
, возвращает вам min
!
Одна вещь, которая неочевидно, что арифметика двухкомпонентных процессоров не имеет понятия положительных или отрицательных чисел!Он рассматривает все числа как неподписанные под капотом.Существует только одна схема сумматора и одна схема умножителя.Схема сумматора работает для положительных и отрицательных чисел, а схема умножителя работает для положительных и отрицательных чисел.
Пример: -1 * -1
= -1 * -1
= (cast both to uint32_t)
= 0xFFFFFFFF * 0xFFFFFFFF
= FFFFFFFE00000001 // if you do the result to 64 bit precision
= 0x00000001 // after you truncate to 32 bit precision
= 1
Единственный раз, когда вы заботитесь о подписанныхvs unsigned - для сравнения, например <
или >
.