【入門編】ビルトイン関数 ADD, SUBTRACT, MULTIPLY, DIVIDE の精度指定 – PL/Iの基本構文とデータ制御実践ガイド

こんにちは!メインフレームの世界へようこそ。
JavaやCOBOLといった他の言語をご経験の方にとって、IBMメインフレームの世界、そしてそこで動く「PL/I(ピーエルアイ)」という言語は、どこか要塞のような、近寄りがたい雰囲気をまとって見えるかもしれませんよね。

「なんだか古い言語ルールが多そう……」
「変なデータ宣言がたくさんあって、桁あふれ(オーバーフロー)で夜間バッチが落ちたらどうしよう……」

そんな不安を抱えていませんか? 大丈夫です、怖がる必要は全くありません!
今回は、PL/Iの数ある強力な機能の中でも、金融や基幹システムの計算処理で絶対に避けて通れない「算術ビルトイン関数(ADD, SUBTRACT, MULTIPLY, DIVIDE)の精度指定」について、一緒に優しく紐解いていきましょう。他言語の常識が、ここではちょっとだけ「PL/I流」に化けますが、コツさえ掴めばすごくロジカルで頼もしい奴らなんですよ。

—

1. 他言語出身者が驚く? PL/Iの「予約語を持たない」自由さと怖さ

まず最初に、PL/Iのちょっとユニークな性格をお話しさせてください。
JavaやCOBOLには「予約語」というものがありますよね。例えば、`IF`やまり `MOVE`、`RESERVED` などの単語は、変数名として使うことができません。コンパイラが「あ、それは命令だな」と混乱してしまうからです。

ところが、PL/Iの仕様(厳密な文脈依存の規則)は、いい意味でチャレンジャーです。なんと、PL/Iには「厳密な意味での予約語」がほとんど存在しません。

どういうことかと言うと、極端な話、計算の命令である `ADD` という単語すら、あなたが「ただの変数名」として宣言して使うことができてしまうのです!

/ えっ、こんな変数名も許されちゃうの?の例 /
DCL ADD FIXED DEC(5, 0); / 変数名に ‘ADD’ を使っちゃった! /
DCL IF FIXED DEC(5, 0); / 変数名に ‘IF’ を使っちゃった! /

「えっ、それじゃあコンパイラが迷子になっちゃうんじゃ……?」と心配になりますよね。
でもご安心ください。PL/Iのコンパイラは非常に優秀で、前後の文脈(コンテキスト)をしっかりと読み取って、「あ、ここは変数だな」「おっ、ここはビルトイン関数の呼び出しだな」と賢く判断してくれます。

とはいえ、実務の現場でこんなスリリングな命名をする人はまずいません(笑)。メンテする人が泣いてしまいますからね。ただ、この「文脈で解釈する」というおおらかで強力な気質が、これから解説するビルトイン関数の世界にも大きく関わってきます。

—

2. なぜ必要? 演算の「精度」と桁あふれ(Overflow)の恐怖

基幹システム、特に金融系の勘定系システムでは、1円の狂いも許されない厳密な計算が求められますよね。
Javaなら `BigDecimal` を使ったり、COBOLなら `COMPUTED BY` や `ROUNDED` を駆使して桁数をコントロールしますが、PL/Iではデフォルトの四則演算(`+`, `-`, “, `/`)を行うと、コンパイラが「良かれと思って」自動的に結果の精度(整数部と小数部の桁数)を決定します。

これが、時と場合によっては思わぬ「桁あふれ(S0C7などのデータ例外や、重大な切り捨てエラー)」を引き起こす原因になります。

例えば、こんな計算を想像してみてください。

  • 3桁の数字 + 3桁の数字 = 最大で何桁になりますか?
  • そう、人間なら「4桁になる可能性があるな」と分かりますよね。

PL/Iの算術ビルトイン関数 `ADD`、`SUBTRACT`、`MULTIPLY`、`DIVIDE` は、単なる計算を行うだけでなく、「この計算結果は、絶対にこの精度(全体何桁、小数部何桁)に収めなさい!」とプログラマが明示的に強制できる、最高に頼れる防波堤なのです。

—

3. 実践! 4つのビルトイン関数と精度指定の作法

それでは、実際のコード例を見ていきましょう。
レガシー移行の現場でよく目にする、固定小数点(`FIXED DECIMAL`)を使った安全な記述スタイルで解説します。

コード例:安全第一の算術ビルトイン関数たち

/ /
/ 算術ビルトイン関数の精度指定サンプルフプログラム /
/ /
TEST_CALC: PROC OPTIONS(MAIN);

/ データ宣言(見やすく大文字で統一しています) /
DCL W_AMT1 FIXED DEC(7,2) INIT(12345.67); / 金額1: 全7桁、小数2桁 /
DCL W_AMT2 FIXED DEC(5,2) INIT(987.50); / 金額2: 全5桁、小数2桁 /

/ 結果を受け取る変数たちの宣言 /
DCL W_ADD_RES FIXED DEC(9,2); / 加算結果用 /
DCL W_DIV_RES FIXED DEC(9,4); / 除算結果用(精度を細かく) /

/ —————————————————- /
/ 1. ADD関数:加算と同時に結果の精度を指定する /
/ —————————————————- /
/ 構文: ADD ( 値1, 値2, 精度(全体桁数, 小数桁数) ) /
W_ADD_RES = ADD(W_AMT1, W_AMT2, 9, 2);

/ [解説]
W_AMT1 と W_AMT2 を足した結果を、強制的に「全体9桁、小数2桁」に
仕立て上げます。これなら桁あふれの心配がありません! /

/ —————————————————- /
/ 2. SUBTRACT関数:減算の精度指定 /
/ —————————————————- /
/ 構文: SUBTRACT ( 値1, 値2, 精度(全体, 小数) ) /
/ ※ W_AMT1 から W_AMT2 を引きます /
W_ADD_RES = SUBTRACT(W_AMT1, W_AMT2, 9, 2);

/ —————————————————- /
/ 3. MULTIPLY関数:乗算(かけ算)で爆発する桁を守る /
/ —————————————————- /
/ かけ算は桁数が一気に増えるため、精度指定が一番重要です! /
/ 構文: MULTIPLY ( 値1, 値2, 精度(全体, 小数) ) /

Dcl W_MUL_RES FIXED DEC(13,4); / 桁が増えるので大きめの受け皿 /
W_MUL_RES = MULTIPLY(W_AMT1, W_AMT2, 13, 4);

/ [解説]
7桁と5桁を掛け合わせると、単純計算で最大12桁の整数部が必要になります。
ここでMULTIPLY関数を使って「全体13桁、小数4桁」と明示しておくことで、
コンパイラに中間計算のルールを正確に伝えることができます。 /

/ —————————————————- /
/ 4. DIVIDE関数:除算(わり算)の無限小数・丸め対策 /
/ —————————————————- /
/ わり算は「割り切れなくて小数点が無限に続く」恐怖がありますよね。 /
/ 構文: DIVIDE ( 被除数, 除数, 精度(全体, 小数) ) /

W_DIV_RES = DIVIDE(W_AMT1, W_AMT2, 9, 4);

/ [解説]
DIVIDE関数では、精度指定が「必須」となります。
これをサボるとコンパイルエラーになるか、予期せぬ切り捨てが発生します。
「小数第4位まで求めてね」と指示することで、システム側で安全に丸め処理を
コントロールできるようになります。 /

END TEST_CALC;

—

4. アーキテクトからのワンポイントアドバイス(実務の知恵)

他の言語からPL/Iの世界に飛び込んだ方へ、現場のシニアアーキテクトとしてこっそりコツを伝授します。

1. 「なんとなくの自動計算」に頼らない
バッチ改修などで「なんか動くからいいや」と素の演算子(`+`, `-`, “, `/`)を複雑に組み合わせると、コンパイラのバージョンアップ時やデータの境界値(MAX値が入ってきたときなど)で突然のABEND(異常終了)を引き起こします。計算が複雑になりそうなときは、迷わず今回紹介したビルトイン関数で精度をガチガチに固めておきましょう。それが夜間バッチをぐっすり眠らせる秘訣です。

2. 変数のデータ属性(属性のスケーリング)を意識する
PL/Iの `FIXED DEC(P, Q)` における `P`(精度)と `Q`(スケール)の意味をしっかり把握しておくと、ビルトイン関数の引数を書くときに迷わなくなります。「全体で何桁持てる箱なのか」「小数点はどこで区切られているのか」、このイメージを頭の中にイラストとして描く癖をつけてみてくださいね。

—

いかがでしたでしょうか?
「PL/Iのビルトイン関数による精度指定」、なんだか難しそうに見えて、実は私たちの計算を守ってくれる心強い味方であることが伝わったなら嬉しいです。

レガシーシステムの移行や保守は、古い仕様との対話の連続ですが、一つひとつ紐解いていけば、当時の設計者の「データを絶対に壊さないぞ」という熱いこだわりが見えてきて、とても面白いものですよ。

それでは、あなたのメインフレームライフが快適で素晴らしいものになりますように。また次回の技術コラムでお会いしましょう!

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