В данной короткой статье будет рассмотрен дополнительный код — способ записи знаковых чисел (как положительных так и отрицательных). Мы не просто рассмотрим формальную сторону вопроса, но и увидим какой смысл и какая интуиция стоит за данным решением, иными словами попытаемся прийти к созерцанию дополнительного кода.

Для того, чтобы в полной мере что-либо понять, нужно это сотворить. Зададимся себе этой целью.

В памяти компьютера мы можем хранить исключительно целые неотрицательные числа с конечной разрядностью. Пускай у нас в распоряжении есть 8 бит, в которые нам нужно уместить по некоторому правилу как положительные, так и отрицательные числа.

Первое, что может прийти в голову — это отводить старший бит под знак, положим, 1 значит, что число отрицательное, а 0 — положительное. Но в таком случае сложив, например, 3 и -1 мы получим:

000000112 + 100000012 = 100001002 = 13210

что отличается от ожидаемой нами двойки. К тому же такой способ записи знаковых чисел допускает существование положительного и отрицательного ноля. Конечно мы бы могли аппаратно обрабатывать подобные знаковые числа, но это бы сильно усложнило разработку процессоров и увеличило бы вычислительные затраты для работы со знаковыми числами. Следовательно, нам нужно придумать такой тип записи, при котором бы мы получали корректный результат при прямом сложении положительного числа с отрицательным.

Логично было бы разделить весь диапазон чисел, доступных нам в восьми разрядах, пополам, чтобы одну половину занимали положительные числа, а вторую обратные к ним по сложению. Возьмем, к примеру, число 3. Попробуем отыскать для него обратное по сложению, то есть такое число, которое при сложении с тройкой даст нам 0. Число 3 в двоичном коде записывается как 00000011. Как же нам с помощью сложения из него получить ноль? Для этого результатом нашего сложения должно быть число 100000000. Так как наше число восьмиразрядное, то самая старшая единичка отбросится, и в наших восьми разрядах мы будем иметь 00000000.

На этом моменте читателю предлагается остановиться и немного подумать, как из 00000011 получить обратное к нему по сложению.

Для того, чтобы из 00000011 получить обратное, нужно для начала инвертировать все его биты (единицу заменить нулем, а ноль — единицей), ибо в таком случае при сложении мы получим 11111111, а затем прибавить единицу, чтобы получить 100000000, чего мы и добивались. Итак, число -3 в дополнительном коде выглядит как 11111101. Очевидно, что подобным образом мы сможем найти обратное число к любому из нашего диапазона 8-битных чисел, ибо мы находимся в кольце классов вычетов по модулю 28 (если не в курсах, что такое кольца классов вычетов — забейте). Для того, чтобы запись каждого числа была однозначной, положительными числам считаются все от 00000001 до 01111111, отрицательными — все от 10000000 до 11111111. Иначе бы мы не знали как интерпретировать число, например, 10000101: то ли как 133, то ли как -123. Таким образом, в наши 8 бит мы можем записать числа от -128 до 127.

Поздравляю, теперь вы знаете, как компьютер представляет отрицательные числа, и не будете дивиться тому, что при инвертировании битов знакового числа вы чудесным образом получаете отрицательное число, по модулю большее на единицу.

Инвертирование битов числа 10 в Python
Инвертирование битов числа 10 в Python

Комментарии (16)


  1. MAXH0
    28.12.2023 16:41
    +2

    Учебник Информатики 8 класс. Студенты уже кончились?


    1. ArtemDudich Автор
      28.12.2023 16:41
      +3

      Нестор Иванович, в учебниках обычно просто формально приводится алгоритм преобразования без объяснения того, почему он выглядит именно так, а не иначе. Я же попытался исправить эту ситуацию, объяснив то, какие побуждения были предприняты теми, кто придумывал дополнительный код. Не каждому повезло взять в руки толковое пособие, или иметь хорошего преподавателя


      1. VT100
        28.12.2023 16:41
        +1

        Спасибо. Но на днях уже была такая статья.


  1. SIISII
    28.12.2023 16:41
    -2

    Не увидел, почему код называется дополнительным :)

    Кроме того, нуль считается положительным числом. Это в математике у него нет знака, а в компутерах очень даже есть.


    1. ArtemDudich Автор
      28.12.2023 16:41

      А можно, пожалуйста, поподробнее: почему это в компуктерах у нуля есть знак?


      1. SIISII
        28.12.2023 16:41

        Потому что с результатом операции (в том числе сравнения или проверки значения) связаны, как правило, четыре пары атрибутов: нуль/не нуль, плюс/минус, перенос/нет переноса, переполнение/нет переполнения. Скажем, если сравниваются два одинаковых числа, будут получены признаки "нуль" (он же равно: сравнение выполняется путём вычитания, только результат собственно вычитания теряется, остаются лишь признаки результата) и "плюс" (поскольку старший бит результата равен нулю). Если сразу после сравнения выполнить команду "переход, если результат положительный", переход произойдёт -- т.е. равенство (нулевой результат вычитания) будет воспринято как положительное число. Это хорошо видно, если писать на ассемблере (см., например, регистр флагов в архитектуре IA-32, она же x86, или регистр состояния процессора в архитектуре ARM).


        1. ArtemDudich Автор
          28.12.2023 16:41

          Мало что понял из вашего "объяснения". Очень путано пытаетесь донести свою мысль

          Будто бы ответ создан нейронкой Яндекса, прогнанной несколько раз через переводчик.

          Если в некоторой архитектуре при условной переходе ноль воспринимается как положительные число, то это не значит, что в компьютерах ноль имеет знак. Частный пример ничего не говорит об общем.

          Да и такой переход можно трактовать, как переход при неотрицательном результате.


          1. SIISII
            28.12.2023 16:41

            Вообще, в большинстве архитектур так, включая IA-32 (x86) и ARM. Ну а вещественный нуль вообще везде знак имеет, похоже.


            1. ArtemDudich Автор
              28.12.2023 16:41

              В архитектуре Risc-V такого перехода условного нет. Вы можете разве что сравнить значение в некотором регистре с нулем.


              1. SIISII
                28.12.2023 16:41

                В MIPS, кажется, тоже. А в IBM System/360 система переходов другая, поэтому там целочисленного знакового нуля, с точки зрения программиста, тоже нет: там ещё не четыре традиционных флажка, а двухбитовый код условия, правила установки которого зависят от типа выполняющейся команды. Поэтому тамошняя система команд имеет отдельные команды сложения-вычитания для знаковых чисел и для беззнаковых: сам результат, понятно дело, идентичен, отличается лишь установка кода условия. Но появились эти машинки ещё в середине 1960-х, у основной массы более современных -- те самые четыре флажка, что оказалось наиболее удобным для программиста.

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


          1. ArtemDudich Автор
            28.12.2023 16:41

            Вы аппелируте к флагу SF, который равен старшему значащему биту результата. Но то, что бит равен 0 лишь значит, что результат неотрицательный, а не положительный. Непонятно откуда вы взяли, что это признак плюса

            UPD:
            Даже рандомная картинка из интернета это подтверждает (см jns)


            1. SIISII
              28.12.2023 16:41

              А, например, в PDP-11 и ARM этот бит называется N (понятное дело, от negative), а для обозначения условия перехода используется сочетание PL -- от plus, естественно:

              Как видите, в этом отрывке (из описания архитектуры ARMv7-M) специально подчёркивается, что "плюс" -- это положительное число или нуль, а не только "настоящее" положительное число.

              В общем, компьютерный мир намного шире, чем Ваше (и моё, да) представление о нём, и абстрактно-математическим концепциям он иногда не соответствует.


              1. ArtemDudich Автор
                28.12.2023 16:41

                Ну это просто название) PL. Какая разница? Чисто ради сокращения могли написать. Просто челиксы решили придумать такое название для флага.


              1. ArtemDudich Автор
                28.12.2023 16:41

                Окей, флаг N - это отрицательное число. Если N равен единице (true), то число отрицательное, если нулю (false), то неотрицательное. Неотрицательное не значит положительное


                1. SIISII
                  28.12.2023 16:41

                  А переход -- по знаку "плюс" (BPL), и нуль рассматривается как имеющий знак "плюс". Неотрицательное не значит положительное только в математике -- напоминаю про существование положительного и отрицательного нуля в вещественных числах. Ну и в целых числах знаковый бит тоже есть, и он сброшен ("плюс") для нулевого значения числа.


      1. SIISII
        28.12.2023 16:41

        И, кстати, если уж касаться "усложнения разработки процессоров", то неплохо было бы спуститься на уровень схемотехники и показать, чем так хорош дополнительный код (а он действительно хорош, иначе б его не использовали). Правда, сразу возникает вопрос: а почему в вещественных числах используют прямой код (и имеют положительный и отрицательный нули)? В общем, улучшать есть куда.