Как в компьютере хранятся отрицательные целые числа?
- Что такое дополнительный код?
- Почему диапазон byte от -128 до 127, а не до 128?
- Как получить -x из x на уровне битов?
Почти везде — дополнительный код (two's complement): старший бит отрицательный, его вес −2ⁿ⁻¹.
- −x = инвертировать все биты и прибавить 1
- Сложение одинаковое для знаковых и беззнаковых — процессору не нужна отдельная схема
- Диапазон n бит: от −2ⁿ⁻¹ до 2ⁿ⁻¹−1, у byte — от −128 до 127, ноль один
А ещё Отрицательных на одно больше, поэтому abs(MIN_VALUE) = MIN_VALUE
Подробно
Целое число в n битах — просто n нулей и единиц. Вопрос в том, как договориться о знаке. Исторически было три способа:
- прямой код — старший бит знак, остальные модуль. Два нуля (+0 и −0) и сложение требует отдельной логики для знаков;
- обратный код — отрицательное число получается инверсией битов. Тоже два нуля;
- дополнительный код — старшему биту приписывается вес −2ⁿ⁻¹, остальные как обычно. Ноль один, а сложение и вычитание работают теми же схемами, что и для беззнаковых чисел: результат просто берётся по модулю 2ⁿ.
Поэтому все современные процессоры используют дополнительный код, а Java, C# и Go закрепляют его в спецификации (в C++ — с C++20).
Как получить −x: инвертировать все биты и прибавить единицу. Для 8 бит: 5 = 0000 0101, инверсия 1111 1010, плюс 1 — 1111 1011 = −5. Проверка: −128 + 64 + 32 + 16 + 8 + 2 + 1 = −5.
Асимметрия диапазона. Битовых комбинаций 2ⁿ, одна занята нулём, поэтому отрицательных на одно больше: в byte от −128 до 127. Отсюда ловушка: Math.abs(Integer.MIN_VALUE) возвращает отрицательное число, потому что +2³¹ не помещается в int.
Расширение знака. При переводе в больший тип старший бит копируется влево: −5 в 16 битах — 1111 1111 1111 1011. Поэтому в Java (int) b для byte −1 даёт −1, а не 255; беззнаковое значение получают маской b & 0xFF.
Сдвиги тоже различаются: >> в Java сохраняет знак (арифметический сдвиг), >>> заполняет нулями (логический).
Пример
def to_bits(x, n=8):
return format(x & (1 << n) - 1, f"0{n}b") # маска: младшие n бит
to_bits(5) # '00000101'
to_bits(-5) # '11111011' = инверсия 5 и +1
to_bits(-128) # '10000000'Что хотят услышать
- Название — дополнительный код — и правило «инвертировать и прибавить 1».
- Почему он победил: один ноль и общая схема сложения.
- Диапазон и асимметрия, abs(MIN_VALUE).
- Расширение знака и беззнаковое чтение через маску — плюс.
Частые ошибки
- Говорить, что старший бит «просто знак», а остальные — модуль.
- Думать, что −x получается только инверсией битов, без +1.
Проверьте себя
Один верный вариант. Подумайте, потом откройте ответ.
Показать варианты и ответ
- ✗Прямой код: старший бит — знак, остальные — модуль числа, поэтому у byte диапазон от −127 до 127 и два нуля, +0 и −0
- ✗В дополнительном коде два нуля: +0 и −0
- ✓Дополнительный код: −x = инвертировать биты и прибавить 1; у byte диапазон от −128 до 127
- ✗abs(MIN_VALUE) всегда положительно
Что спросят дальше
Связанные вопросы
Разбор собеседования с sotto
После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.