Дополнительный код: как байт хранит отрицательные числа

В проводе нет знака «минус». Есть только высокое и низкое напряжение, 1 и 0. Откуда же в 8-битном процессоре берётся −5, если байт умеет хранить лишь числа от 0 до 255? Для этого инженеры придумали соглашение, которое называется дополнительным кодом (two's complement). Оно настолько удачное, что все современные процессоры хранят отрицательные числа именно так.

5 инверсия (НЕ) +1 00000101 11111010 11111011 это +5 это −5 Проверка: 5 + (−5) = 00000101 + 11111011 = 1 00000000 Девятый бит (перенос) отбрасывается — остаётся 00000000, ноль. Тождество сходится
Дополнительный код числа −5: инвертировать все восемь битов пятёрки и прибавить 1. Сложение +5 и −5 даёт ноль — знак «работает» в обычном сумматоре

Проблема: в проводе нет минуса

Простейшее решение — отдать один бит под знак: старший бит 0 — число положительное, 1 — отрицательное. Такой «прямой код» понятно читается человеком, но ломает железо: сложение байта +5 и −5 в прямом коде не даёт нуля, и сумматору понадобилась бы отдельная логика для знаков — другой, более медленный тракт.

Дополнительный код устраняет проблему в корне: отрицательные числа устроены так, что обычный сумматор складывает их правильно, даже не зная о знаках. Отдельный вычитатель в процессоре не нужен вообще.

Как получить −N: инверсия и +1

Правило для 8-битного байта:

1. Записать число N в обычном двоичном виде: 5 = 00000101.
2. Инвертировать все восемь битов (НЕ над каждым): 11111010.
3. Прибавить единицу: 11111010 + 1 = 11111011.

Готово: 11111011 — это −5. Обратный перевод тот же: инверсия и +1 (11111011 → 00000100 → 00000101 = 5). Старший бит у отрицательных чисел всегда 1 — он «бесплатно» работает индикатором знака, и именно его проверяет флаг Negative из статьи о флагах состояния.

Колесо значений: от −128 до 127

Удобнее всего почувствовать дополнительный код как колесо: байт умеет 256 значений, и мы просто разрезали круг не 0…255, а так: 0…127 — положительные, дальше 128…255 читаются как −128…−1. Прибавляя единицу, мы движемся по колесу; 127 + 1 = −128 — «переехали» через границу, это и есть переполнение знака (его ловит отдельный флаг в настоящих процессорах).

Из-за такой разметки диапазон несимметричен: положительных 127, а отрицательных 128. Ноль один — у него единственное представление, в отличие от прямого кода, где +0 и −0 записывались по-разному.

Вычитание через сложение

Теперь главное следствие: A − B = A + (−B). Чтобы вычесть 10 из 25 в 8-битном АЛУ, процессор берёт −10 (11110110), подаёт оба байта на тот же самый сумматор и получает 00001111 — пятнадцать. Никакой новой схемы: те же полные сумматоры, что складывают обычные числа.

Именно так устроен уровень «Проверка на минус» в курсе: программа вычисляет 5 − 10, получает 251 (= −5 в дополнительном коде), а флаги N и Z показывают, что результат ушёл в минус. И тем же приёмом пользуется ассемблер: команды вычитания там нет — есть ADD отрицательного числа.

Проверить любой байт в дополнительном коде можно в конвертере систем счисления — он показывает знак и диапазон.

Проверь себя

Как записать −1 в дополнительном коде одного байта?

Инверсия 00000001 даёт 11111110, прибавляем 1 — 11111111. Минус один — это все единицы.

Почему вычитатель не нужен отдельной схемой?

Потому что A − B = A + (−B), а −B получается инверсией и +1. Обычный сумматор складывает их корректно без какой-либо знаковой логики.

Что случится, если к 127 прибавить 1 в знаковом байте?

Получится −128: колесо переполнено. Положительных значений ровно 127, и шаг за их границу «прыгает» на самый низ диапазона.

Попробовать в симуляторе →