Как работает дополнительный код
У компьютеров нет знака минус — только биты. Дополнительный код — это соглашение, которое почти каждый CPU использует для хранения отрицательных целых: старший бит (знаковый) считается как отрицательное 2ⁿ⁻¹, а не положительное. В 8 битах старший бит стоит −128, поэтому 1111 1111 = −128 + 64 + 32 + 16 + 8 + 4 + 2 + 1 = −1, а 1000 0000 — это −128.
Те же биты, прочитанные как беззнаковые, равны 255 и 128. Является ли значение «на самом деле» отрицательным, решает тип в вашем коде (int8_t против uint8_t), а не биты. Поэтому этот инструмент всегда показывает оба прочтения.
Как получить отрицательное число вручную
- Запишите положительное значение в двоичном виде при полной разрядности (42 =
0010 1010). - Инвертируйте каждый бит (
1101 0101). - Прибавьте 1 (
1101 0110). Это −42, то есть 0xD6 или 214 без знака.
Приём работает в обе стороны: применив его к −42, вы снова получите 42. Инструмент выше выводит эти шаги для любого введённого значения.
Почему он устроен именно так
С дополнительным кодом аппаратуре не нужен отдельный вычитатель. Сложение с отрицательным числом — это обычное двоичное сложение, в котором перенос из старшего бита отбрасывается: 42 + (−42) = 0010 1010 + 1101 0110 = 1 0000 0000, отбрасываем девятый бит, получаем 0. Кроме того, существует только один ноль, в отличие от прямого и обратного кода.
Диапазоны
| Разрядность | Знаковый диапазон | Беззнаковый диапазон | Типичный тип |
|---|---|---|---|
| 8 | −128 … 127 | 0 … 255 | int8_t, byte |
| 16 | −32 768 … 32 767 | 0 … 65 535 | int16_t, short |
| 32 | −2 147 483 648 … 2 147 483 647 | 0 … 4 294 967 295 | int32_t, int |
| 64 | −9,22 × 10¹⁸ … 9,22 × 10¹⁸ | 0 … 1,84 × 10¹⁹ | int64_t, long |
Отрицательный диапазон на единицу больше положительного, потому что ноль занимает один из «положительных» шаблонов. Поэтому -INT_MIN переполняется: 128 не помещается в знаковый байт.
Вопросы
Что такое −1 в дополнительном коде?
Все единицы: 0xFF в 8 битах, 0xFFFF в 16, 0xFFFFFFFF в 32. Инвертирование 0000 0001 даёт 1111 1110, прибавление 1 даёт 1111 1111.
Как понять, что двоичное число отрицательное?
Посмотрите на самый левый бит при объявленной разрядности. Если он равен 1 и значение знаковое, число отрицательное. 1000 0000 — это −128 как int8, но 128 как uint8; сами по себе биты ничего не решают.
Почему 127 + 1 даёт −128?
Знаковое переполнение. 0111 1111 + 1 = 1000 0000, что устанавливает знаковый бит. В C это неопределённое поведение; в большинстве других языков значение заворачивается к −128.
Что такое расширение знака?
Расширение знакового значения копированием знакового бита в новые старшие биты, так что −1 (0xFF) становится 0xFFFF, а не 0x00FF. Приведение int8 к int16 делает это автоматически; приведение беззнакового значения вместо этого дополняет нулями. Введите отрицательное число и переключайте разрядность выше, чтобы это увидеть.
Знаковый бит — это просто флаг?
Нет. В дополнительном коде знаковый бит имеет значение (−2ⁿ⁻¹) и участвует в арифметике. Этим он отличается от прямого кода, где старший бит лишь отмечает знак, и от чисел с плавающей точкой, где знак — отдельный флаг.