Дополнительный код: представление отрицательных чисел
Дополнительный код (two's complement) - это способ записывать целые числа со знаком в двоичной системе так, чтобы вычитание превращалось в обычное сложение, а одна и та же схема сумматора работала и с положительными, и с отрицательными числами. Именно поэтому почти все процессоры хранят знаковые целые именно в дополнительном коде, а не в прямом или обратном. Ниже разберём, как записать отрицательное число в фиксированной разрядности, почему старший разряд получает отрицательный вес, как переводить число в код и обратно тремя способами и где в задачах возникает переполнение. Чтобы сразу почувствовать связь между числом и его битами, покрути калькулятор ниже: он показывает дополнительный код в двоичном и шестнадцатеричном виде, вклад каждого разряда и положение числа на кольце кодов.
Зачем нужен дополнительный код
В прямом коде знак числа хранится в старшем бите: 0 - плюс, 1 - минус, а остальные разряды задают модуль. Просто, но неудобно: появляются два нуля (положительный и отрицательный), а вычитание требует отдельной логики сравнения знаков. Дополнительный код избавляется от обоих недостатков. В нём отрицательное число записывается так, что его сложение с положительным даёт правильный результат на том же сумматоре, который складывает беззнаковые числа. Ноль остаётся единственным, а диапазон используется полностью.
Главная идея: при фиксированной разрядности бит все коды живут по модулю . Отрицательное число представляется тем же кодом, что и беззнаковое . Например, в 8 битах число кодируется как , то есть все восемь единиц. Поэтому при переборе кодов от вверх мы доходим до максимума, а затем «переваливаем» в отрицательную область - отсюда и образ кольца.
Как старший бит получает отрицательный вес
Самое короткое определение дополнительного кода: это обычное позиционное двоичное представление, в котором вес старшего разряда взят со знаком минус. Для бит значение числа равно
где - значение -го бита. Все младшие разряды дают привычные положительные веса , и только старший разряд весит . Если он равен нулю, число положительно и совпадает с беззнаковым; если единице - к сумме добавляется большое отрицательное слагаемое, и число уходит в минус.
Возьмём канонический пример: 8-битный код . Старший бит равен 1, значит вклад старшего разряда . Остальные единицы стоят в разрядах 6, 4, 1 и 0, давая . Итог: . Тот же код, прочитанный как беззнаковое число, равен , а в шестнадцатеричной записи это .

На схеме весов хорошо видно, почему дополнительный код «самосогласован»: чтение по весам и есть обратное преобразование. Не нужно отдельно смотреть на знак - достаточно просуммировать вклады разрядов, помня, что самый левый из них отрицательный.
Как перевести число в дополнительный код
Для отрицательного числа есть три эквивалентных пути, и в калькуляторе они дают один и тот же результат.
Первый - по формуле остатка. Код отрицательного числа равен беззнаковому представлению величины . Для и это , то есть .
Второй - инверсия и плюс единица, самый удобный «ручной» способ. Записываем модуль числа в обычном двоичном виде, инвертируем все биты (получаем обратный код), затем прибавляем единицу:
Третий - через подбор по весам: подобрать единицы в разрядах так, чтобы их веса (с отрицательным старшим) дали нужное число. На практике им пользуются для проверки, а не для расчёта.

Чтобы перевести любое своё число в код и обратно, задай разрядность и подвинь ползунок в калькуляторе выше: он покажет двоичную и шестнадцатеричную запись, разложение по весам и положение числа на кольце кодов.
Диапазон представимых чисел и его несимметричность
При битах в дополнительном коде помещаются числа от до :
Для 8 бит это от до , для 16 бит - от до . Диапазон несимметричен: отрицательных чисел на одно больше, чем положительных. Причина проста - один из кодов занят нулём, и он лежит в «положительной» половине, поэтому самому большому по модулю отрицательному числу нет положительной пары. Отсюда известная ловушка: модуль числа в 8 битах нельзя представить как , а попытка взять снова даёт .
Сложение, вычитание и переполнение
Главная награда за дополнительный код - арифметика. Чтобы вычесть, достаточно прибавить дополнительный код вычитаемого: . Перенос из старшего разряда просто отбрасывается, потому что вычисления идут по модулю . Это и позволяет одному сумматору обслуживать оба знака.
Опасность одна - переполнение. Оно возникает, когда истинный результат выходит за диапазон . Признак переполнения: при сложении двух чисел одного знака получился результат противоположного знака. Например, в 8 битах , но , поэтому код «перескакивает» через границу и читается как . Программный или аппаратный флаг переполнения как раз ловит ситуацию, когда перенос в старший разряд и перенос из него не совпадают.
Частые ошибки
- Инверсия без прибавления единицы. После инвертирования битов получается обратный код, а не дополнительный. Единицу прибавлять обязательно, иначе число будет меньше на единицу.
- Игнорирование разрядности. Дополнительный код не существует «вообще»: - это в 4 битах и в 8 битах. Без указания запись бессмысленна.
- Перевод старшего бита как обычного. Старший разряд весит , а не . Если прочитать его с плюсом, отрицательные числа превратятся в большие положительные.
- Симметричный диапазон. Граница не , а от до . Число есть, а его положительной пары нет.
- Незамеченное переполнение. Отброшенный перенос - это норма, а вот смена знака при сложении одинаковых знаков - сигнал, что результат недостоверен.
FAQ
Как перевести отрицательное число в дополнительный код вручную? Запишите модуль числа в двоичном виде в нужной разрядности, инвертируйте все биты и прибавьте единицу. Для в 8 битах: , инверсия , плюс единица - .
Почему старший бит называют знаковым, если у него есть и вес? Потому что он одновременно указывает знак и вносит вклад в значение. У положительных чисел он равен нулю, у отрицательных - единице, но это не отдельный флаг, а полноценный разряд с отрицательным весом.
Чем дополнительный код лучше прямого и обратного? В нём единственный ноль и не нужна отдельная логика для вычитания: оно сводится к сложению. Прямой и обратный коды дают два нуля и требуют коррекции при переносе, поэтому в процессорах прижился именно дополнительный код.
Коротко
Дополнительный код представляет знаковые целые так, что вычитание становится сложением, а старший разряд весит . Отрицательное число получают по формуле или инверсией битов модуля с прибавлением единицы; обратное чтение - это сумма по весам разрядов. Диапазон при битах равен от до и несимметричен, а выход за него даёт переполнение, заметное по неожиданной смене знака.
Читайте также

Кодирование звука: частота дискретизации и теорема Найквиста
Как работает кодирование звуковой информации: частота дискретизации, теорема Найквиста-Шеннона, разрядность, алиасинг и расчёт размера аудиофайла с примерами.

Дополнительный код: представление отрицательных чисел
Как работает дополнительный код в двоичных системах: диапазон значений, перевод отрицательного числа, разбор по весам разрядов, переполнение и типичные ошибки в задачах.

230 пространственных групп симметрии: откуда берётся число
230 пространственных групп симметрии в кристаллографии: как из 32 точечных групп, 14 решёток Браве и трансляций получается ровно 230 групп Фёдорова, и зачем это нужно.