| Упражнение #1. |
|
Постройте таблицу сложения для троичной
уравновешенной системы счисления. |
Что нам дают все эти рассуждения об операциях в
уравновешенных системах счисления? Оказывается, понятие
дополнение актуально и для "обычных" позиционных нумераций,
когда речь идет об отрицательных числах. Так, если отрицательное
число -S записано в системе счисления с
алфавитом At, и занимает m
разрядов, то его дополнение определяется как tm-S.
Располагая m-разрядным
диапазоном, имеющим мощность tm,
половину его предоставим положительным числам, включая 0, и половину
- отрицательным. При таком подходе можно оперировать положительными
и отрицательными числами, чей модуль не превосходит tm-1 (за исключением самого числа tm-1, взятого со знаком '+').
Положительные числа 0 .. tm-1-1 останутся на своих местах, а
отрицательные - из диапазона -tm-1
.. -1 - будут представлены своими дополнениями
и попадут в диапазон tm-1 .. tm-1. Например, для числа -2510 (t=10; m=2) его дополнением является (102-25)10=7510.
| Упражнение #2. |
|
Как выглядит дополнение для числа -12310 в 4-й, 8-й,
10-й, 16-й системах счисления при m=4,
8? |
Особенно удобным оказывается использование
описанного выше механизма для компьютерной арифметики,
базирующейся на двоичной системе счисления. Можно, конечно,
осуществлять перевод из формы представления "отрицательное число -S2" в форму "дополнение 2m-S2" непосредственно, но
тогда приходится использовать операцию "вычитание", а мы-то как раз
хотим от нее избавиться. Оказывается, есть возможность несложного
преобразования из обычной формы представления в дополнительный код и
обратно, без использования вычитания. Остановимся на этом подробнее.
Для хранения целых чисел в памяти выделяется
фиксированное число двоичных разрядов (бит = bit = binary
digit) - обычно это степень 2.
Рассмотрим, для простоты, самый компактный вариант
хранения - 8-битный. Такой однобайтный диапазон допускает 28 различных возможных значений. Если
использовать его "почти симметрично" для положительных и
отрицательных чисел, то мы располагаем диапазоном 0..127 для
положительных чисел (включая "неотрицательный 0") и, соответственно,
-128..-1 - для отрицательных.
Нумеруем биты в байте от 0 до 7 - "справа налево",
если рассматривать их в привычной для нас форме, - в соответствии со
степенями 2.
Размещаем в байте абсолютную величину числа в
двоичной форме - то есть его двоичный код. Если теперь в старший бит
- то есть, 7-й - поместить '0' (в роли знака "плюс") либо '1' (в
качестве "минуса" ) то полученное представление называется
прямым двоичным кодом. Нетрудно заметить, что положительное
число, превосходящее 12710,
"залезет" в знаковый бит и, соответственно, код окажется неверным.
Кстати говоря, число -12810 в битах
0..6 также не поместится, и будет в прямом коде выглядеть как '10000000', что можно прочесть и как "-010".
Роль старшего бита здесь двоякая: кроме функции
"знака", этот бит, если рассматривать содержимое байта как
положительное число, соответствует степени 27, то есть помещает прямой код
соответствующего отрицательного числа в диапазон 12810 .. 25510.
Далее, - если "обнаруживается", что прямой код
соответствует отрицательному числу (о чем свидетельствует именно 7-й
бит), - инвертируем (заменой '0' на '1' и наоборот) все разряды,
кроме знакового, и получаем обратный двоичный код.
Фактически, так формируется число, дополняющее абсолютную величину
исходного до 27-1 (что происходит
для числа -12810, проанализируйте
сами) - его называют еще двоичным дополнением со сдвигом.
Наконец, после добавления к обратному коду 1,
получается дополнительный двоичный код. (Для -12810 прямой и дополнительный коды
совпадают.)
| Пример #1. |
|
Число 2510,
размещаемое в одном байте, будет выглядеть как 000011012. Соответственно, -2510 в прямом ко де равно 100011012. Инвертируя, получаем
обратный код 111100102.
Добавляя затем 1, переходим к дополнительному
коду 111100112.
|
Нетрудно заметить, что сложение 2510 и -2510 даст в результате 0. Лишний, 8-й
бит, получающийся при переполнении, физически не с уществует, что
нас вполне устроит.
Впрочем, в зависимости от архитектуры конкретной
ЭВМ и/или используемого программного обеспечения, факт переноса
единицы из старшего разряда влево можно фиксировать в специальном
флаге переполнения. (Например, в системе программирования
Borland Pascal можно установить контроль за
диапазоном значений с помощью директивы $R+.) И
далее, продолжая вычисления в рамках компьютерной программы,
вызвавшей указанное изменение флага, можно учитывать его состояние.
Итак, вводимые для обработки десятичные числа
переводятся в двоичный дополнительный код и в таком виде затем
обрабатываются. Но, проведя необходимую обработку, неплохо бы еще
"вернуться" от представления в дополнительном коде к прямому, чтобы
перевести результат в привычную для пользователя компьютера 10-ную
нумерацию. Как это сделать?
Если знаковый бит дополнительного кода содержит 1
(то есть, число отрицательное), то для результата выводится знак
"минус", затем следует инвертировать разряды, получая обратный код,
а затем вновь добавить 1, что приведет нас к прямому коду абсолютной
величины двоичного числа.
Таким образом, манипулируя двоичными числами в
дополнительных кодах, мы избавляемся, как и стремились, от операции
"вычитание".
Убедимся в "дееспособности" алгоритма на том же
примере.
Дополнительный код числа -2510 равен 111100112.
Так как в старшем разряде стоит 1, то число -
отрицательное.
Инвертируя разряды 0..6, получим обратный код 100011002.
Добавляя 1, переходим к прямому коду абсолютной
величины 100011012.
В результате имеем число -2510.
Укажу еще, что, при наличии 0 в знаковом бите,
описанные выше манипуляции не производятся, поскольку число
"распознается" как положительное, то есть не требующее перевода в
дополнительный двоичный код.
| Упражнение #3. |
| a) |
Как выглядят в дополнительном коде десятичные
числа -127, -1, -0? |
| b) |
Сравните представления в обратном и в
дополнительном коде десятичных чисел -0 и +0. |
| c) |
Сделайте несколько упражнений по "машинному"
вычислению разностей вида a-b, подставляя на место a и b
различные значения из разрешенного диапазона.
|
Однако вернемся к ситуации переполнения старшего из
m разрядов, выделенных для представления целого числа. Рассмотрим
следующий
| Пример #2. |
|
Пусть в нашем распоряжении 2 десятичных
разряда (т.е. m=2), используемых только
для неотрицательных значений (диапазон 0..99), и
осуществляется сложение чисел 5610+7810. Очевидно,
вместо "правильного" ответа 13410 мы получим 3410. С точки зрения обычной
арифметики, это соответствует получению целочисленного остатка
от деления числа 13410 на
мощность выделенного диапазона: (134 mod
100)10. |
Кстати говоря, когда в математике идет речь о
подобном механизме, называемом модульной арифметикой, часто
используют запись такого вида: mod (134, 100).
Понять, что здесь происходит, несложно, если
представить себе, что числа от 0 до 9910 выписаны друг за другом на
отрезке ленты, а затем концы склеены так, что получится кольцо .
Соответственно, после числа 9910
будет следовать 0.
Теперь посмотрим, как работает модульная арифметика
в двоичной нумерации, но пока без использования дополнительных
кодов. При m=8 (1 байт) тот же пример в
"двоичном воплощении" выглядит следующим образом:
т.е. 13410 и "все в порядке".