Получить доступ

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

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

Так подсказку видит пользователь sotto на живом собеседовании — за пару секунд, пока вы думаете вслух.

Попробуйте сами

Число изнутри: дополнительный код и плавающая точка

Подробно

Целое число в 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.

Что спросят дальше

Связанные вопросы

Закрытая бета

Подсказки на собеседовании — в реальном времени

sotto слышит вопрос интервьюера и показывает короткий ответ и план, как в блоке «Коротко». Оставьте контакт — пришлём доступ и промокод.

  • Промокод на 2 часа — сверх пробных 30 минут
  • Программа для Windows — скачать сразу, войти — через Яндекс ID или VK ID
  • Прямая связь с разработчиком: что улучшить — сделаем