RPG

565. フリー・フォーマット演算の闇

今日も重要な告知である。

次の演算命令を見て欲しい。

/FREE
  TOPRRN = (BRRN / SFLPAG) * SFLPAG + 1;
/END-FREE

フリー・フォーマットであるので、まとめて一行で書いて
これは楽。フリー・フォーマットにして良かった … ?
 

[解説]

と思いきや、これは全く正しく動作しない。
TOPRRN = (81 / 15) x 15 + 1 = 76 であるはずなのに
この演算結果は TOPRRN = 82 となってしまう。
こんな単純な計算がこのフリー・フォーマットの演算では誤動作を
おこしてしまうのだろうか?

 
 
BRRNをSFLPAGEで割り算するとAIは次の理由で正しく動かないと
説明してくれた。

 
1行の評価式 (BRRN / SFLPAG) * SFLPAG + 1 の場合、式全体が完了するまで
一時的な中間結果(Intermediate Result)としてデータが保持されます。

式の中の (BRRN / SFLPAG) は、型変換されずに高精度な小数桁を持った中間結果(1.5)として
メモリ上に残ります。

そのため、その後に * SFLPAG を掛けると 1.5 * 10 = 15 に戻ってしまい、
最終結果は 15 + 1 = 16 となってしまいます。

小数部の切り捨てが最終的に TOPRRN へ代入される最後の一回しか行われないため、
意図した「ページの切り捨て計算」になりません。

[解決案1] %DIV 組み込み関数を使用する(推奨)

商の整数部分のみを返す %DIV(Division)を使用すると、余りを切り捨てて1行で計算できます。

/FREE
  TOPRRN = %DIV(BRRN : SFLPAG) * SFLPAG + 1; 
/END-FREE

[解決案2] %INT や %FLOOR で明示的にキャストする

除算結果を即座に整数に変換します。

/FREE
  TOPRRN = %INT(BRRN : SFLPAG) * SFLPAG + 1; 
/END-FREE

[解説]

つまり今までの固定フォーマットの演算では切り捨てられていた演算が
フリー・フォーマットの連続演算では保持されているので
次の演算も小数以下も併せて演算が継続するので結果的に誤動作を引き起こすと
いうわけである。
しかし (81 / 15) x 15 + 1 = 5.4 x 15 + 1 = 81 + 1 = 82 であるから
フリー・フォーマットの演算は間違っているのではなく数学上では正しい。
しかし私たちRPGプログラマーは 81 / 15 = 5 余り 4 と固定フォーマットの
演算に慣習的に慣れ切っているので求める演算結果ではないので
フリー・フォーマット自身がバグを起こしているわけではない。
しかしこれは固定フォーマットからフリー・フォーマットへ移行するときの
思わぬ落とし穴になる可能性がある。

なおAIが示した2つの解決策はどちらも正しく動作することを確認している。
フリー・フォーマットの割り算では / ではなくて %DIV を使うことと覚えておけばよい。