As400 CL скрипт арифметики дает 0

Я пытаюсь вычислить остаток от деления с помощью алгоритма:

remainder = dividend - (dividend / divisor) * divisor

Все рассчитано в целых числах.

Пример: получить остаток от 15 / 6.

1. (15 / 6) = 2
2. (2) * 6 = 12
3. 15 - 12 = 3

Остальная часть 15/6 действительно 3.

Моя проблема заключается в использовании этого алгоритма в моем сценарии CL просто возвращает 0 все время. Это почему?

pgm
dcl var(&dividend) type(*int) value(15)
dcl var(&divisor) type(*int) value(6)
dcl var(&remainder) type(*int)

dcl var(&msg) type(*char)

/* Calculate remainder. [ed: 29Sep2016] "* &remainder" is now: "* &divisor" */ 
chgvar var(&remainder) value(&dividend - (&dividend / &divisor) * &divisor)

/* Prior to 29-Sep-2016 redaction, the above was coded as the incorrect expression: +
chgvar var(&remainder) value(&dividend - (&dividend / &divisor) * &remainder)       +
   and remains, commented-out, to preserve relevance of user-comments about the OP */

/* Cast remainder and display. */
chgvar var(&msg) value(&remainder)
sndpgmmsg msg(&msg)

endpgm

Составлено с:

crtclpgm pgm(test) srcfile(test) srcmbr(test)

Бежать:

call test

Выход:

0

2 ответа

Решение

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

&dividend - (&dividend / &divisor) * &divisor
= 15 - (15 / 6) * 6
= 15 - 2.5 * 6
= 15 - 15
= 0

Чтобы это исправить, я сделал следующее:

chgvar     var(&remainder) value(&dividend / &divisor)
chgvar     var(&remainder) value(&dividend - &remainder * &divisor)  

Описанный результат является побочным эффектом / это было закодировано как оператор деления. / оператор не является оператором целочисленного деления в CL; некоторые языки могут предложить // в качестве дополнительного оператора деления для этого эффекта, или, возможно, в качестве скалярной функции, такой как DIV; хотя, когда язык предлагает такие дополнительные арифметические функции, они, вероятно, также предлагают связанный скаляр, такой как MOD или же REM напрямую получить остаток.

Листинг IRP показал, что CL работает так, как если бы он был явно закодирован как имеющий промежуточное значение с ненулевой шкалой; фактически TYPE(*DEC) LEN(24 9), но CL допускает только 15 цифр точности, поэтому я использую TYPE(*DEC) LEN(15 5) в этом примере программы, которая имитирует создание промежуточного результата для деления перед выполнением остальной части выражения:

pgm
 dcl var(&dividend) type(*int) value(15)
 dcl var(&divisor) type(*int) value(6)
 dcl var(&remainder) type(*int)
 dcl var(&intermedP) type(*dec) len(15 5)
/* Calculate remainder. *two-step process -- mimics IRP listing; yields 0  */
chgvar var(&intermedP) value(&dividend / &divisor) /* P15,5 intermed result */
chgvar var(&remainder) value(&dividend - &intermedP             * &divisor  )


Потому что правильный результат требует, чтобы (&dividend / &divisor) [как часть выражения &dividend - (&dividend / &divisor) * &divisor] должен приводить к целочисленному результату, что означает, что промежуточный результат для этой операции деления должен быть принудительно установлен на тип *INT или на другой числовой тип с нулевым масштабом.
И, как показывает уже принятый ответ, следующая версия для использования целого числа для промежуточного результата решает проблему:

pgm
 dcl var(&dividend) type(*int) value(15)
 dcl var(&divisor) type(*int) value(6)
 dcl var(&remainder) type(*int)
 dcl var(&intermedI) type(*int)
 /* Calculate remainder. *two-step process; per change, correctly yields 3  */
chgvar var(&intermedI) value(&dividend / &divisor) /* *INT  intermed result */
chgvar var(&remainder) value(&dividend - &intermedI             * &divisor  )

В отличие от того, что я упомянул в комментарии 24Sep к OP, нет возможности кодировать следующее выражение для достижения желаемого результата всего лишь одним оператором CHGVAR; первое выражение не проходит проверку синтаксиса с помощью msg CPD0058 "Встроенная функция%INT допускает 1 аргумент". и второй сбой с сообщением CPD0181 "Аргумент для встроенной функции%INT недопустим." И я ожидаю того же эффекта [но я не проверял] от кодирования встроенного%DEC [с указанием нулевой шкалы; т.е. ноль указан для аргумента десятичных знаков ]:

( &dividend - %int(&dividend / &divisor) * &divisor )

( &dividend - %int((&dividend / &divisor)) * &divisor )
Другие вопросы по тегам