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

Урок раскрывает главный трюк машинной арифметики: дополнительный код для отрицательных чисел.

Дополнительный код — способ хранить отрицательные числа так, чтобы вычитание превращалось в обычное сложение.

Проблема знака

В регистре лежат только биты — никакого «минуса» там нет. Как же хранить −5? Наивная идея «выделить один бит под знак» приводит к двум нулям (+0 и −0) и усложняет сложение. Поэтому процессоры используют дополнительный код: элегантную схему, где плюс и минус складываются одной и той же командой add.

Правило получения отрицательного

Чтобы получить −x: возьми двоичную запись x, инвертируй все биты и прибавь 1. На 8 битах для числа 5:

     5  = 0000 0101
  инверсия 1111 1010
     + 1  ----------
    -5  = 1111 1011

Старший бит у отрицательных всегда 1 — это и есть знаковый бит. Но магия в том, что складывать их можно как обычные числа.

Как работает под капотом

Проверим, что 5 + (−5) даёт 0 при сложении 8-битных кодов, и как одни и те же биты читаются по-разному:

def to_signed8(bits):
    return bits - 256 if bits & 0x80 else bits

five = 0b00000101          # 5
neg_five = (~five + 1) & 0xFF   # дополнительный код -5
print("биты -5:", format(neg_five, '08b'))
print("как знаковое:", to_signed8(neg_five))

total = (five + neg_five) & 0xFF   # 5 + (-5)
print("5 + (-5) =", total)

bits = 0b11111111          # одни и те же биты
print("беззнаково:", bits, "знаково:", to_signed8(bits))

Вывод:

биты -5: 11111011
как знаковое: -5
5 + (-5) = 0
беззнаково: 255 знаково: -1

Обратите внимание на последнюю строку: одни и те же биты 11111111 — это 255 как беззнаковое число и −1 как знаковое. Сами биты не «знают» свой знак; знак появляется только от того, как мы решили их читать.

Частые ошибки

  • Думать, что в битах хранится знак отдельно. Знак — это интерпретация старшего бита, а не отдельное поле.
  • Путать jb/ja и jl/jg. Одни переходы для беззнаковых, другие для знаковых — потому что 0xFF это и 255, и −1.
  • Забывать про разрядность. −5 в 8 битах и в 64 битах выглядит по-разному (число единиц в начале разное).

Итог

  • Отрицательные числа хранятся в дополнительном коде: инверсия битов плюс 1.
  • Старший бит у отрицательных равен 1 (знаковый бит).
  • Одни и те же биты читаются как знаковое или беззнаковое число.
  • Дополнительный код позволяет складывать плюс и минус одной командой.
Проверьте себя
1. Как получить дополнительный код числа −x?
AИнвертировать биты x
BИнвертировать биты x и прибавить 1
CПоставить старший бит в 1
DВычесть x из нуля по разрядам без переноса
2. Чему равны биты 11111111 при разной интерпретации (8 бит)?
A255 и 255
B255 беззнаково и −1 знаково
C−1 и −255
D127 и −128
3. Зачем нужен дополнительный код?
AЧтобы числа занимали меньше памяти
BЧтобы сложение работало одинаково для положительных и отрицательных
CЧтобы ускорить умножение
DЧтобы хранить дробные числа