Перейдем теперь к рассмотрению арифметических операцией и операций сравнения над числами, представленными в десятичной форме. Все команды, реализующие выполнение такого типа операций, имеют формат SS с раздельным указанием длины каждого операнда.
Мнемонические обозначения десятичных команд легко запомнить: они получаются из мнемоник соответствующих двоичных команд добавлением символа Р.
Операция |
Мнемоника команд двоичной арифметики |
Мнемоника команд десятичной арифметики |
Сложение |
А |
АР |
Вычитание
|
S |
SP |
Умножение
|
М |
МР |
Деление
|
D |
DP |
Сравнение
|
С |
СР |
В этих командах, так же как и во всех командах с раздельным указанием длин операндов, в качестве последних можно задавать области, размер каждой из которых не превышает 16 байтов. Поскольку каждый байт, за исключением последнего, может нести информацию о двух цифрах десятичного числа, то, следовательно, максимальное количество цифр десятичного операнда равняется 31.
Рассмотрим десятичные команды более подробно.
Десятичные сложения и вычитания
Команды АР и SP предназначены соответственно для выполнения сложения и вычитания двух десятичных чисел, одно из которых расположено по адресу D1+(B1) и имеет длину L1 байтов, а другое — расположено по адресу D2+(B2) и имеет длину L2 байтов.
АР D1(L1,B1),D2(L2,B2) |
Add Packed |
D1+ (B1)?(D1+ (B1))L1+ (D2 + (B2))L2 |
SP D1(L1 ,B1),D2(L2,B2) |
Subtract Packed |
D1+(B1)?(D1 + (B1))L1— (D2 + (B2))L2 |
Результат выполнения каждой из команд помещается в поле первого операнда, при этом он выравнивается по правой границе. Само выполнение не оказывает влияния на содержимое второго операнда, за исключением случая, когда области первого и второго операндов перекрываются.
По команде
АР NUM1(3),NUM2(2)
двухбайтовое десятичное число, хранящееся в памяти по адресу NUM2, складывается с трехбайтовым числом, хранящимся по адресу
NUM1. Если сначала
NUM1: 75632С
NUM2: 493С
то результатом выполнения приведенной выше команды будет
NUM1: 76125С
Если начальное содержимое областей NUM1 и NUM2 то же, что и прежде, то выполнение команды
SP NUM1(3),NUM2(2)
даст
NUM1: 75139С
Аналогично пусть
AT: 00368D
ВАТ: 336С
После выполнения команды
АР АТ+1 (2), В АТ+1(1)
получим
AT: 00362D
а после выполнения команды
SP ВАТ(2),АТ(3)
получим
ВАТ: 704С
Если хотя бы один из операндов не имеет надлежащего формата, т.е. содержит недопустимые коды знака или цифр, фиксируется особый случай в данных (0С7). Ошибки такого типа, пожалуй, наиболее часто встречаются при использовании десятичной арифметики. Если подобная ошибка была зафиксирована в вашей программе, прежде всего, проверьте правильность указания длин операндов. Следствием ошибочного указания длины операнда может быть определение ошибочного кода знака. Если длины указаны правильно, используйте информацию дампа для выяснения, соответствующее ли содержимое имеют операнды на момент фиксации ошибки. Имейте в виду, что неверное указание длин операндов, а также неправильная подготовка исходных данных в команде PACK может привести к появлению некорректных десятичных данных. Это может проявиться как ошибка значительно позже первоначального ввода и преобразования входных данных.
Если результат выполнения десятичного сложения или вычитания не помещается в поле, определенном в качестве первого операнда, фиксируется особый случай десятичного переполнения (ОСА). Например, результат выполнения команды
АР NINE(l), = Р'Г
при
NINE: 9С
равный ЮС, не может быть помещен в однобайтовую область. Отметим, что сказанное, конечно, не означает, что поле первого операнда команды такого типа должно быть достаточным для записи всех результатов, которые могут получиться при выполнении данной команды. Фактически выполнение проходит следующим образом: сначала производится вычисление результата, а затем попытка поместить его в отведенную область. Если попытка удалась, то независимо от реальной длины операнда команда считается удачно выполнившейся. Пусть, например,
N1: 958С
N2: 000004 2С
Выполнение команды
АР N1(2),N2(4)
приведет к фиксации особого случая в данных, в то время как выполнение команды
SP N1(2), N2(4)
не вызовет особого случая. Признаки результата вырабатываются командами АР и SP так же, как и всеми остальными арифметическими командами:
Признак результата |
Результат |
0 |
Результат = 0 |
1 |
Результат < 0 |
2 |
Результат > 0 |
3 |
Переполнение |
Десятичные умножение и деление
По команде МР (УМНОЖЕНИЕ ДЕСЯТИЧНОЕ) вычисляется произведение двух десятичных чисел и результат помещается в память на место первого операнда.
МР D1(L1,B1),D2(L2,B2) |
Multiply Packed |
D1+(B1)?(D1+(В1))L1X(D2 + (B2)L2 |
Команда МР может быть успешно выполнена лишь тогда, когда область первого операнда достаточна для записи получившегося произведения. На самом деле к размерам области первого операнда предъявляются более строгие требования: в эту область должен помещаться результат выполнения команды МР, дополненный спереди незначащим нулем. Невыполнение этого условия влечет за собой фиксацию особого случая спецификации при попытке выполнения команды.
Попытаемся понять, что означает это условие. Будем называть первый операнд команды МР множимым, а второй — множителем. Количество значащих цифр произведения, очевидно, не превышает суммы количеств значащих цифр множимого и множителя (коды знака здесь не учитываются). Пусть первый операнд команды МР выглядит как
38D
а второй
430С
Произведение
16340D
имеет пять значащих цифр, однако для выполнения описанного выше условия спереди его следует дополнить нулем:
016340D
Множимое имеет две значащие цифры, множитель — три. Проверка выполнения описанного условия производится до начала непосредственного умножения: множимое спереди дополняется четырьмя незначащими нулями. Первый операнд до начала выполнения умножения должен иметь вид
000038D
Это гарантирует, что результат выполнения операции, дополненный спереди незначащим нулем, поместится в отведенную для хранения первого операнда- область. Короче говоря, множимое должно содержать столько незначащих нулей, сколько цифр во множимом, включая код знака. Приведенная в качестве примера ситуация осложняется тем, что, строго удовлетворив необходимому условию, мы можем получить произведение, занимающее нецелое количество байтов. Итак, на самом деле для вычисления произведения
38D
и
430С
необходимо предварительно иметь первый операнд в виде
0000038D
Несмотря на то, что условие, предъявляемое к размерам первого операнда, описано нами достаточно строго, на практике не всегда бывает легко его выполнить, поскольку количество значащих цифр в операндах может быть заранее неизвестно. Поэтому условие, хотя и менее строгое, но зато более удобное для практического применения, может быть переформулировано так: число незначащих нулей множимого должно быть не меньше, чем удвоенное число байтов, занимаемых множителем.
Возвращаясь к нашему примеру, можно видеть, что длина множителя (в байтах) равна двум. Следовательно, множимое должно быть дополнено спереди четырьмя нулями. Фактически мы получили тот же результат, что и раньше, только более простым способом. Если длина второго операнда равна 5 байтам, то, следовательно, необходимо добавить к множимому десять нулей и т. д.
Существует естественное ограничение на длину множителя: эта длина не должна превышать 15. Отметим, что длина множимого, как нетрудно видеть, должна всегда быть больше длины множителя.
Все сказанное говорит о том, что выполнение команды МР обычно требует некоторой специальной подготовки. Предположим, что мы хотим вычислить произведение 5-байтового числа, хранящегося в памяти по адресу NUM1, и 6-байтового числа, хранящегося по адресу NUM2. Ясно, что произведение может не уместиться в NUM1. Для обеспечения возможности нормального выполнения команды МР необходимо каким-то образом дополнить первый операнд спереди двенадцатью нулями. Простейшим способом достижения этой цели является пересылка содержимого NUM1 в область, первые шесть байтов которой уже содержат нули. (В подобных случаях очень полезной оказывается команда ZAP, рассматриваемая в разд. 16.3.1.) Итак, получить требуемое произведение можно, например, с помощью команд
MVC TEMP + 6(5), NUM1
МР ТЕМР(11), NUM2
TEMP DC 11Х'00'
Предположим, что содержимое областей NUM1 и NUM2 первоначально выглядело следующим образом:
NUM1: 1038000 10C
NUM2: 60402000001D
Результатом выполнения приведенных выше команд, помещенным в поле TEMP, будет
TEMP: 006269728204123800010D
По команде DP (ДЕЛЕНИЕ ДЕСЯТИЧНОЕ) производится деление одного десятичного числа (делимого), адрес которого указан на месте первого операнда, на другое (делитель), адрес которого указан на месте второго операнда команды DP. Получившиеся в результате деления частное и остаток записываются в память на место делимого.
DP D1 (L1 ,В 1),D2(L2,B2) |
Divide Packed |
Вычисляется (D1+(B1))L1?(D2+(B2))L2частное? (D1 +(B1))L1-L2 остаток ? (D1 + (В 1) + L 1 — L2)L2 |
Остановимся подробнее на том, как производится запись результата в память. Заметим, что длина остатка не может превышать длины делителя. Например, если в качестве делителя выступает число 608С, то остаток от деления не может превышать 607С. Если делитель занимает в памяти L2 байтов, то остаток никак не может занимать больше этого количества. После выполнения деления по команде DP получившийся остаток записывается в последние L2 байтов первого операнда. В первые L1—L2 байтов записывается частное. Десятичную операцию деления, так же как и двоичную, можно охарактеризовать следующей формулой:
делимое = делитель х частное + остаток
Знак остатка совпадает со знаком делимого. Частное положительно, если знаки делимого и делителя совпадают, в противном случае частное отрицательно.
Теперь мы знаем достаточно много для того, чтобы рассмотреть пример. Пусть
DIVIDND: 05703С
DIVISR: 608С
и производится деление первого числа на второе. Частное и остаток равны соответственно
9С
и
231C
Длина делителя равна 2, длина делимого — 3. Остаток помещается в последние L2=2 байта области DIVIDND. Частное помещается в первый (LI—L2=l) байт этой >де области. Итак, при данном содержимом областей DIVIDND и DIVISR выполнение команды
DP DIVIDND(3),DIV1SR(2)
даст
DIVIDND: 9С231С
Аналогично, если
DIVIDND: 000307685D
DIVISR: 007G3C
то результатом выполнения
DP DIVIDND(5),DIVISR(3)
будет
DIVIDND: 403D00196C
Заметим, что если исходные данные будут те же, то результат выполнения команды
DP DIVI DND(5), DIVISR+ 1(2)
окажется равным
DIVIDND: 00403D196C
Если по сравнению с делимым делитель достаточно мал, то отведенного места может не хватить для размещения вычисленного частного. Например, если
Р: 37605С
Q: 608С
то частное равно
61С
Если команда
DP P(3),Q(2)
выполняется с такими начальными данными, то несложные вычисления показывают, что для записи частного будет отведен лишь один байт. В данном случае, однако, частное не может быть размещено в столь малом поле. В подобных случаях фиксируется особый случай десятичного деления. Возникновение этой ситуации можно исключить, зарезервировав несколько большее место для хранения делимого.
Признак результата в процессе выполнения команд МР и DP не меняется.
Команда CP
По команде СР (СРАВНЕНИЕ ДЕСЯТИЧНОЕ) производится вычисление разности двух десятичных чисел, длина каждого из которых указывается отдельно. В соответствии со знаком получившейся разности устанавливается признак результата. Содержимое памяти в процессе выполнения команды не меняется,
СР D1 (LI ,B1),D2(L2,B2) |
Compare Packed |
Устанавливается значение СС в соответствии со знаком разности (D1 +(B1))L1-(D2 + (B2))L2 |
Признак результата вырабатывается следующим образом:
Признак результата |
Условия |
0 |
Первый операнд = второй операнд |
1 |
Первый операнд < второй операнд |
2 |
Первый операнд >второй операнд |
3 |
He встречается |
Если операнды команды СР имеют различную длину, то для обеспечения возможности выполнения вычитания более короткий операнд дополняется спереди соответствующим количеством нулей. Например, если
N1: 05683С
N5: 9С
и выполняется команда
СР N1(3),N5(1)
то в качестве реальных операндов выступают
05683С
00009С
соответственно. При выполнении операции считается, что необходимые нули уже внесены. В нашем примере в качестве значения признака результата будет выработано число 2. Это свидетельствует о том, что первый операнд больше, чем второй, или, что то же самое, что при вычитании второго операнда из первого получится положительный результат. Если первоначально
N1 00005С
N5 9С
то той же самой командой будет выработан признак 1; это же произойдет, если будет выполнена команда
СР МЗ(2),М7(5)
при условии, что
М3: 400D
М7: 000000500D
При выполнении команды СР производится проверка на правильность подготовки исходных данных. Если хотя бы один из операндов не является правильным упакованным десятичным числом, фиксируется особый случай в данных.