Дополнительный код/запись отрицательных чисел в памяти компьютера
В данной короткой статье будет рассмотрен дополнительный код — способ записи знаковых чисел (как положительных так и отрицательных). Мы не просто рассмотрим формальную сторону вопроса, но и увидим какой смысл и какая интуиция стоит за данным решением, иными словами попытаемся прийти к созерцанию дополнительного кода.
Для того, чтобы в полной мере что-либо понять, нужно это сотворить. Зададимся себе этой целью.
В памяти компьютера мы можем хранить исключительно целые неотрицательные числа с конечной разрядностью. Пускай у нас в распоряжении есть 8 бит, в которые нам нужно уместить по некоторому правилу как положительные, так и отрицательные числа.
Первое, что может прийти в голову — это отводить старший бит под знак, положим, 1 значит, что число отрицательное, а 0 — положительное. Но в таком случае сложив, например, 3 и -1 мы получим:
000000112 + 100000012 = 100001002 = 13210
что отличается от ожидаемой нами двойки. К тому же такой способ записи знаковых чисел допускает существование положительного и отрицательного ноля. Конечно мы бы могли аппаратно обрабатывать подобные знаковые числа, но это бы сильно усложнило разработку процессоров и увеличило бы вычислительные затраты для работы со знаковыми числами. Следовательно, нам нужно придумать такой тип записи, при котором бы мы получали корректный результат при прямом сложении положительного числа с отрицательным.
Логично было бы разделить весь диапазон чисел, доступных нам в восьми разрядах, пополам, чтобы одну половину занимали положительные числа, а вторую обратные к ним по сложению. Возьмем, к примеру, число 3. Попробуем отыскать для него обратное по сложению, то есть такое число, которое при сложении с тройкой даст нам 0. Число 3 в двоичном коде записывается как 00000011. Как же нам с помощью сложения из него получить ноль? Для этого результатом нашего сложения должно быть число 100000000.Так как наше число восьмиразрядное, то самая старшая единичка отбросится, и в наших восьми разрядах мы будем иметь 00000000.
На этом моменте читателю предлагается остановиться и немного подумать, как из 00000011 получить обратное к нему по сложению.
Для того, чтобы из 00000011 получить обратное, нужно для начала инвертировать все его биты (единицу заменить нулем, а ноль — единицей), ибо в таком случае при сложении мы получим 11111111, а затем прибавить единицу, чтобы получить 100000000, чего мы и добивались. Итак, число -3 в дополнительном коде выглядит как 11111101. Очевидно, что подобным образом мы сможем найти обратное число к любому из нашего диапазона 8-битных чисел, ибо мы находимся в кольце классов вычетов по модулю 28 (если не в курсах, что такое кольца классов вычетов — забейте). Для того, чтобы запись каждого числа была однозначной, положительными числам считаются все от 00000001 до 01111111, отрицательными — все от 10000000 до 11111111. Иначе бы мы не знали как интерпретировать число, например, 10000101: то ли как 133, то ли как -123. Таким образом, в наши 8 бит мы можем записать числа от -128 до 127.
Поздравляю, теперь вы знаете, как компьютер представляет отрицательные числа, и не будете дивиться тому, что при инвертировании битов знакового числа вы чудесным образом получаете отрицательное число, по модулю большее на единицу.
Инвертирование битов числа 10 в Python