Вопрос с собеседования · Основы

Как в компьютере хранятся отрицательные целые числа?

JuniorЧисла в компьютере
Ещё спрашивают так:
  • Что такое дополнительный код?
  • Почему диапазон 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 минут бесплатно.

Разбор собеседования