Дополнительный код: отрицательные числа
Урок раскрывает главный трюк машинной арифметики: дополнительный код для отрицательных чисел.
Дополнительный код — способ хранить отрицательные числа так, чтобы вычитание превращалось в обычное сложение.
Проблема знака
В регистре лежат только биты — никакого «минуса» там нет. Как же хранить −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 (знаковый бит).
- Одни и те же биты читаются как знаковое или беззнаковое число.
- Дополнительный код позволяет складывать плюс и минус одной командой.