Вычитание двоичных целых знаковых чисел Старший бит называется знаковым – 0 для положительных чисел и 1 для отрицательных, а остальные биты являются значащими. Традиционно для представления отрицательных чисел используются обратный и дополнительный коды, а вычитание заменяется сложением. При этом обратный код является инверсией пря - мого кода, а дополнительный образуется прибавлением положительной единицы к обратному коду. Недостат - ком прямого и обратного кодов считается наличие двух нулей. Для прямого кода в случае однобайтных чисел это 00000000 (положительный) и 10000000 (отрицательный). Для обратного кода положительный нуль 00000000, а отрицательный нуль 11111111. Для дополнительного кода только один нуль – 00000000. Однако обратный код обладает неустранимым недостатком в обработке нулей. При вычислениях, когда результат равен нулю, мы всегда получаем отрицательный нуль (11111110 + 00000001 = 11111111). Положи - тельный нуль получается только в случае сложения отрицательного нуля с единицей (11111111 + 00000001 = 00000000), что абсолютно некорректно с точки зрения математики. Поэтому обратный код будет рассматри - ваться как дополнение к прямому. Для простоты изложения и понимания рассматривается вычитание только положительных чисел. Прямое двоичное вычитание производится аналогично сложению. Оба варианта приведены в таблице 1. Таблица 1. Исходная операция x + y x – y x 0 0 1 1 x 0 0 1 1 y 0 1 0 1 y 0 1 0 1 Сумма 0 1 1 0 Разность 0 1 1 0 Перенос 0 0 0 1 Перенос 0 1 0 0 Как видно из таблицы 1, для расчёта суммы и разности используется одна и та же битовая операция XOR, различие операций только в методе формирования бита переноса. Особенностью вычислений в прямом коде является необходимость проведения операций только с опе - рандами одного знака. Поэтому для операндов разных знаков приходится применять замену операций, пока - занную в таблице 2. Таблица 2. Исходная операция x + y x – y Операнды Знаки операндов Операнды Знаки операндов x + + – – x + + – – y + – + – y + – + – Замена x + y x – (–y) x – (–y) x + y Замена x – y x + (–y) x + (–y) x – y Как указывалось выше, стандартно вычитание заменяется сложением, при этом второй операнд y преоб - разуется в дополнительный код инверсией всех бит с дальнейшим добавлением к нему положительной едини - цы. Результат всегда представлен в правильном (прямом или дополнительном) коде. Операция добавления по - ложительной единицы не является отдельной операцией, она осуществляется как бит переноса 1 для младше - го бита в основной операции сложения. Знаковые биты обрабатываются таким же образом, как и значащие. Вся операция занимает один такт процессора. Это достоинство дополнительного кода – самые быстрые опе - рации сложения и вычитания. Но нет достоинств без недостатков. К недостаткам дополнительного кода относится несимметричный диапазон отрицательных и положительных чисел, поэтому для однобайтных чисел требуется отдельная обра - ботка числа -128 (двоичная запись 10000000). Вычисление модуля этого числа нельзя представить в прямом коде однобайтного числа, также его формирование не подчиняется общим правилам формирования чисел в дополнительном коде. Это требует обработки этого случая либо в микрокоде процессора, либо на уровне ран - тайма программы. Отсутствие двух нулей приводит к тому, что математические программы вынуждены в рас - чётах эмулировать отрицательный нуль, что делается на уровне рантайма. Такие дополнительные проверки и эмуляции снижают скорость работы программы. Также к недостаткам дополнительного кода можно отнести то, что проверка на переполнение результата при сложении осуществляется не только по биту переноса из старшего значащего бита в знаковый бит, но и по биту переноса из знакового бита. В прямом коде знаковые биты обрабатываются не так, как в дополнительном коде. При вычитании, если есть бит переноса из старшего значащего бита в знаковый бит, результат отбрасывается, а при сложении бит переноса из старшего значащего бита в знаковый бит служит индикатором переполнения, и в этом случае об - работка знакового бита результата оговаривается соглашением между производителями процессоров или не оговаривается никак. Например, если при переполнении не менять знак результата, то будет кольцевой буфер размером 128 адресов (для однобайтных чисел адреса). К недостаткам прямого кода относится то, что в случае, когда модуль вычитаемого больше модуля уменьшаемого, результат вычитания представлен в дополнительном коде и для его представления в прямом коде необходимо делать инверсию значащих бит результата с прибавлением положительной единицы. Это приводит к тому, что вычитание в прямом коде в данном случае медленнее, чем в дополнительном (два такта вместо одного). Целью данной работы является разработка способа, который бы позволил осуществить вычитание в прямом коде так же быстро, как и в дополнительном, то есть за один такт процессора. Решением этой задачи является одновременное вычитание по правилам, изложенным в таблице 1, для операндов в прямом и обратном кодах с выбором результата по отсутствию бита переноса из старшего знача - щего бита в знаковый бит. Поясним это на примере: в прямом коде 25 – 47 = 00011001 – 00101111 = 11101010. Поскольку есть перенос из старшего значащего бита в знаковый бит, этот результат отбрасывается (даже не - смотря на то, что знак результата правильный). В обратном коде тот же результат 25 – 47 = 11100110 – 11010000 = 10010110. Нет переноса из старшего значащего бита в знаковый бит, поэтому этот результат вы - даётся как результат расчёта. Для отрицательных чисел аналогичные вычисления по вышеизложенным прави - лам показывают, что в прямом коде (-25) – (-47) = 10011001 – 10101111 = 01101010 (результат отбрасывается, несмотря на правильный знак результата), для обратного кода 01100110 – 01010000 = 00010110 (результат вы - даётся как результат расчёта). Но есть один вырожденный случай, когда в обоих случаях нет бита переноса из старшего значащего бита в знаковый бит. Это возможно тогда, когда оба операнда одинаковы и результат по модулю равен нулю. С учётом этого правило выбора результата меняется – результат вычитания в прямом коде выбирается тогда, когда нет бита переноса из старшего значащего бита в знаковый бит. В противном слу - чае выбирается результат вычитания в обратном коде. Это упрощает схему выбора результата. Обработка зна - кового бита не нужна, при отсутствии бита переноса из старшего значащего бита в знаковый бит он просто копируется из любого из операндов в результат. Из вышесказанного может показаться, что для реализации данного решения необходимо два вычитаю - щих устройства. Но если вспомнить, что x XOR y = y XOR x = (NOT x) XOR (NOT y) = (NOT y) XOR (NOT x), то схема формирования результата будет одинакова. Будут вычисляться два результата по прямым и инвер - тированным битам исходных операндов и два бита переноса. В конце расчёта выбирается один из результатов по отсутствию бита переноса из старшего значащего бита в знаковый бит. Реализация этого алгоритма позволит осуществлять операцию вычитания в прямом коде за один такт процессора. Программная эмуляция показала, что увеличение времени выполнения вычитания в прямом коде по сравнению с временем вычитания в дополнительном коде составляет не более 10 %. Это вызвано последова - тельным выполнением действий. Учёт обработки переполнений при сложении уменьшает этот разрыв ещё на 2-3 %. В случае параллельности выполнения действий различия во времени не будет. К достоинствам прямого кода относится и более лёгкое детектирование переполнения результата при сложении: достаточно только бита переноса из старшего значащего бита в знаковый бит. Также к ним отно - сится симметричный диапазон отрицательных и положительных чисел, что приводит к отсутствию выро - жденных чисел и их обработки в рантайме. Обработка двух нулей нужна только при выводе результата на внешнее устройство (например, экран или файл). В случае умножения целых чисел в дополнительном коде используется алгоритм Бута. Он требует обра - ботки знакового бита, потому что обрабатывает биты парами. Поэтому число итераций равно количеству раз - рядов числа (8 для однобайтных чисел). В прямом коде знаковый бит является только меткой и обрабатывает - ся параллельно основному циклу вычисления, поэтому в прямом коде циклов вычисления будет на один мень - ше разрядности чисел (7 для однобайтных чисел). Выбор между прямым и дополнительным кодами должен проводиться с учётом всех достоинств и недо - статков использования каждого кода не только на уровне процессора, но и на уровне рантайма, потому что пользователь оценивает скорость программы в совокупности.