【入門編】MOD関数とREM関数の除算命令展開の違い – PL/Iの基本構文とデータ制御実践ガイド

こんにちは!メインフレームの世界へようこそ。
JavaやCOBOLといったモダン、あるいはビジネス標準の言語をバリバリ書いてきた方にとって、レガシーの代名詞とも言える「PL/I(ピーエルアイ)」のソースコードを初めて目の当たりにした瞬間というのは、なかなか強烈なインパクトがありますよね。

「なんだこの暗号のような大文字の羅列は…」「変数名のルールはどうなっているんだ?」と、不安になるお気持ち、痛いほどよく分かります。でも大丈夫です。怖がる必要はまったくありません。今日は、PL/Iのちょっとユニークで奥深い「識別子のルール」に軽く触れつつ、多くのエンジニアが思わずハマる「剰余(余り)計算の罠」について、一緒に紐解いていきましょう。

—

1. 予約語を持たない!?PL/Iの懐の深さと変数名のルール

JavaやCOBOLを触ってきた方なら、「`if`や`while`、あるいは`display`といったキーワードを変数名に使ってはいけない(予約語だから)」というのは常識ですよね。

ところが、PL/Iの言語仕様には、なんと「固定の予約語(Reserved Words)が存在しない」という驚きの大方針があります。

「えっ、じゃあ `IF` という名前の変数を作ったら、コンパイラはどうやって判断するの?」と思いますよね。
PL/Iのコンパイラは、その名の通り「文脈(コンテキスト)」をものすごく賢く読み解きます。

1
/ こんな変てこなコードも、PL/Iなら文法エラーになりません /
DCL IF FIXED BIN(31) INIT(10);
DCL THEN FIXED BIN(31) INIT(20);

IF = THEN + 5;

……はい、実務でこんな変数名をつけたら、コードレビューで確実に大目玉を食らいますし、後輩から絶交されますので絶対にやめましょう(笑)。ただ、PL/Iという言語がどれほど柔軟に設計されているか、その空気感が伝われば幸いです。

私たち実務の現場では、可読性を守るために他の言語と同様に意味のある識別子(変数名)を使います。そして、これからお話しする「除算と剰余」の処理でも、この言語特有の数学的なこだわりが顔を出します。

—

2. MOD関数とREM関数:何が違うの?

さて、本日のメインテーマです。
バッチプログラムの中で「割り算の余り」を求めたいとき、あなたならどうしますか?
Javaであれば `%` 演算子を使いますし、COBOLなら `DIVIDE … REMAINDER …` を使いますよね。

PL/Iにも剰余を求めるための組み込み関数(Built-in Function)が用意されています。それが `MOD` と `REM` です。

「どっちも余りを出すんでしょ? 同じじゃないの?」

そう思ってJavaの感覚のまま移行設計を行うと、本番稼働後に痛い目を見ます。実はこの2つ、「負の数(マイナス値)を割ったときの挙動」がまったく異なるのです。

メインフレームのハードウェア背景をちょっとだけ覗き見

IBMメインフレーム(System zなど)のCPUには、整数同士の割り算を行う専用の機械語命令(`DR`命令や`DRS`命令など)が存在します。
メインフレームのハードウェアレベルにおける除算命令は、数学的な「剰余(Modulo)」ではなく、「商の整数部を切り捨て(Truncation)、その余りを返す」という設計になっています。

このハードウェアの挙動を忠実に再現するもの、それが `REM`(Remainder:剰余/余り)関数 です。
一方で、数学的に厳密な定義に基づいた周期的な剰余を返すのが `MOD`(Modulo)関数 です。

—

3. 具体例でスッキリ理解!負の数が絡む計算の挙動

百聞は一見にしかず。実際にPL/Iのコードでその違いを確認してみましょう。
以下のサンプルコードを見てください。

1
//
/ プログラム名: DIVTST /
/ 概要 : MOD関数とREM関数の挙動の違いを確認するサンプル /
//
DIVTST: PROC OPTIONS(MAIN);

/ 変数の宣言:符号付きFIXED BINARY(31) 31ビット整数 /
Dcl A Fixed Bin(31) Init( -7 );
Dcl B Fixed Bin(31) Init( 3 );

Dcl Res_Mod Fixed Bin(31);
Dcl Res_Rem Fixed Bin(31);

/ MOD関数の計算 (-7 を 3 (MOD) する) /
Res_Mod = Mod(A, B);

/ REM関数の計算 (-7 を 3 で割った余り) /
Res_Rem = Rem(A, B);

/ 結果の出力 /
Put Skip Edit (‘A = ‘, A, ‘B = ‘, B) (A(4), F(4), A(4), F(4));
Put Skip Edit (‘MOD結果 = ‘, Res_Mod) (A(10), F(4));
Put Skip Edit (‘REM結果 = ‘, Res_Rem) (A(10), F(4));

End DIVTST;

このプログラムを実行したとき、画面(SYSOUT)には何が表示されるでしょうか?

  • `MOD( -7, 3 )` の結果: ` 2`
  • `REM( -7, 3 )` の結果: `-1`

「えっ、同じ `-7` を `3` で割っているのに、余りがプラスになったりマイナスになったりするの!?」と驚かれたのではないでしょうか。

なぜこのような違いが出るのか?

  • `REM`(Remainder:余り)の考え方
  • `-7 ÷ 3` を普通に計算すると、商は `-2.333…` です。
  • 商の小数点を切り捨て(ゼロ方向へ丸め)ると `-2` になります。
  • 逆算してみましょう: `3 × (-2) = -6`
  • 元の数 `-7` に戻すためには、`-6` に `-1` を足す必要がありますよね。
  • だから、`REM` の結果は `-1` になります(符号は割られる数 `-7` と同じになります)。
  • `MOD`(Modulo:剰余)の考え方
  • 数学的なモジュロ演算は、割る数(今回は `3`)の周期でループする世界を考えます。
  • `3` の倍数で `-7` に最も近い下側の数は `-9` です。
  • `-9` から `-7` までいくつ増えているかを見ると、`-9 + 2 = -7` となり、余りは `2` になります(符号は割る数 `3` と同じ、またはそれに準ずる形になります)。

—

4. マイグレーションやバッチ改修時の実務アドバイス

他言語(Javaの `%` 演算子など)からPL/Iへ、あるいはその逆へとロジックを移植する際、この違いを見落としていると、金額の端数計算や、日付・サイクルのローテーション判定などのバグを生む原因になります。

1. Javaの `%` や C言語の `%` は、実はPL/Iの `REM` に近い挙動(切り捨て方向の余り)を示します。
2. もし元の設計仕様書に「数学的な剰余(負数であっても常に正の範囲や一定の規則で循環するもの)」と書かれている場合は、単純に `REM` を使うと痛い目を見ます。必要に応じて `MOD` を選択するか、自前で正値に補正するロジックを挟む必要があります。

レガシーシステムの移行では、「コードの見た目をそのまま置き換える」だけでなく、「その命令が背負っている数学的・ハードウェア的な背景」まで踏み込んで検証することが何よりも大切です。

最初は呪文のように見えるPL/Iの構文や関数も、こうして背景を一つずつ紐解いていけば、決して怖いものではありません。むしろ、プログラマの意図に合わせて細かく道具を使い分けられる、非常に頼もしい相棒に見えてきませんか?

今日の解説が、あなたのメインフレームライフ、そしてスムーズなマイグレーション作業の小さな助けになれば幸いです。それではまた、次の現場でお会いしましょう!

タイトルとURLをコピーしました