【実務・中級編】SQRT関数の浮動小数点演算ユニット(FPU)利用 – PL/Iの基本構文とデータ制御実践ガイド

おい、最近入った若手が「PL/Iって変数名に予約語が使えない制限が緩くて気持ち悪いんですが、これってバグりませんか?」なんて聞いてきたんだ。笑っちまうよな。
確かにPL/Iには、COBOLみたいな厳格な「予約語(Reserved Word)」の概念がほとんどない。`IF` や `THEN` さえも、コンテキストによっては変数名として通ってしまう(コンパイラが文脈で判断するからな)。だが、そんな自由度の裏で、ハードウェアの限界や例外処理のメカニズムを理解していないと、深夜のバッチ異常終了(いわゆるS0C4やU4038など)で痛い目を見るのは我々エンジニアだ。

今回は、メインフレームの心臓部であるハードウェア浮動小数点演算ユニット(FPU)と、PL/Iの組込み関数(BUILTIN)、そして避けて通れない「例外処理(ONユニット)」の切っても切れない関係について、現場のノウハウを交えて徹底的に解説してやる。しっかりついてきな。

—

1. 現場の勘所:なぜSQRT関数とFPUの連携が重要なのか

金融や流通の基幹システムで、今なお稼働し続けるPL/Iバッチ。リスク管理モデルの計算や、大規模な統計処理などで平方根を求める `SQRT` 関数を使う場面は少なくない。

IBMメインフレーム(z/Architecture)において、浮動小数点演算は専用のハードウェア機構(FPF: Floating-Point Facility、あるいは近年のベクトル・ファシリティー)であるFPUが高速に処理する。`SQRT` 関数を `BUILTIN` 宣言してコーディングすると、コンパイラはこれをインライン展開し、最終的にマシン語の平方根演算命令(例えば `SQRT` 命令や `SQRTF` 命令など)をきれいに吐き出してくれる。

ここで問題になるのが 「無効な引数(負の数など)」 が渡されたときのハードウェア挙動だ。
数学的に実数の範囲でマイいナスの平方根は存在しない。これをFPUに直接放り込むと、ハードウェア割り込み(浮動小数点例外:指数/データ例外)が発生する。対策をしていないと、プログラムは無慈悲にABEND(異常終了)し、オペレータから叩き起こされる夜間バッチの保守担当者の悪夢が始まるわけだ。

—

2. PL/IのONユニットによる優美な例外トラップ

PL/Iの真骨頂は、このハードウェア例外やランタイムエラーを構造化して捕捉・制御できる ON-Unit(ON条件処理) にある。

COBOLでこれをやろうとすると、ファイルステータスや複雑な条件分岐の山になるが、PL/Iであれば `CONDITION` や `ZERODIVIDE`、`CRIT` といったシステム条件に対して、専用の割り込みハンドラをエレガントに記述できる。

平方根の計算で負数が入り込むリスクがある場合、我々は `ON FIXED` や `ON ERROR`、あるいはより安全に事前チェックと `ON CONDITION` を組み合わせる。だが、ハードウェアFPUの恩恵を最大限受けつつ、異常値を安全にキャッチするには、BUILTIN関数の適切な活用 と ON単位のスコープ管理 が鉄則だ。

—

3. 実践!SQRT関数と例外処理を組み込んだPL/Iサンプルコード

百聞は一見に如かずだ。実際のメインフレーム開発現場(Enterprise PL/Iコンパイラ前提)でそのまま通用する品質のコードを用意した。大文字ベースの記述、適切なインデント、そしてJESのログに出力するためのメッセージ制御を含めている。

SQRT_CALC: PROC OPTIONS(MAIN);

/ ————————————————– /
/ 変数宣言部 /
/ ————————————————– /
DCL IN_VAL DEC FLOAT(16); / 入力値(ロングフロート) /
DCL OUT_VAL DEC FLOAT(16); / 計算結果 /
DCL RC_MSG CHAR(80) VARYING; / ログ出力用メッセージ /

/ 組み込み関数(BUILTIN)の明示的宣言 /
DCL SQRT BUILTIN;
DCL ERROR CONDITION;

/ ————————————————– /
/ 例外処理(ONユニット)の定義 /
/ ハードウェアまたはランタイムでエラーが発生した際の /
/ フォールバック処理をここでトラップする /
/ ————————————————– /
ON ERROR
BEGIN;
PUT SKIP EDIT (‘[SEVERE] 浮動小数点演算エラーを検知しました。入力値=’, IN_VAL)
(A, F(20,6));
OUT_VAL = 0E0; / 異常時の安全なデフォルト値(ゼロ)を設定 /
GOTO CALC_ERR_EXIT;
END;

/ ————————————————– /
/ 処理メインループ(例としてモックデータを処理) /
/ ————————————————– /
PUT SKIP EDIT (‘— 浮動小数点演算(SQRT)テスト開始 —‘) (A);

/ テストケース1: 正常系 (144.0の平方根) /
IN_VAL = 144.0E0;
CALL EXEC_SQRT(IN_VAL, OUT_VAL);

/ テストケース2: 異常系 (ハードウェア例外を引き起こす負数) /
IN_VAL = -25.0E0;
CALL EXEC_SQRT(IN_VAL, OUT_VAL);

GOTO NORMAL_EXIT;

CALC_ERR_EXIT:
PUT SKIP EDIT (‘[WARNING] エラー発生のため処理を安全にバイパスしました。’) (A);

NORMAL_EXIT:
PUT SKIP EDIT (‘— 浮動小数点演算テスト終了 —‘) (A);
RETURN;

/ ————————————————– /
/ 内部プロシージャ: SQRT実行部 /
/ ————————————————– /
EXEC_SQRT: PROC(P_IN, P_OUT);
DCL P_IN DEC FLOAT(16) PARM;
DCL P_OUT DEC FLOAT(16) PARM;

/ 現場の鉄則:FPUに丸投げする前にアプリ側でガードを入れるのが王道 /
IF P_IN < 0E0 THEN DO; PUT SKIP EDIT ('[CAUTION] 負数が検知されました。SQRTスキップ. 入力=', P_IN) (A, F(15,4)); P_OUT = 0E0; RETURN; END; / ハードウェアFPUのSQRT命令を呼び出し(BUILTIN活用) / P_OUT = SQRT(P_IN); PUT SKIP EDIT ('INPUT:', P_IN, ' ==> SQRT RESULT:’, P_OUT) (A, F(15,4), A, F(15,4));

END EXEC_SQRT;

END SQRT_CALC;

—

4. シニアアーキテクトからのアドバイス(デバッグのコツ)

上記のコードを見て、「おっ、ちゃんとアプリ側で `IF P_IN < 0E0` でガードしているじゃないか」と思ったそこの君。鋭い。 実務の現場では、「ハードウェアの例外割り込み(ONユニット)にすべてを頼らない」 のがプロのアーキテクトのやり方だ。
なぜなら、FPUの例外割り込みを毎回トラップしてソフトウェア制御に落とし込むと、オーバーヘッド(コンテキストスイッチやOS側の割り込み処理コスト)が発生し、大量データを処理するバッチのパフォーマンスがガタ落ちするからだ。

1. 事前ガード(アプリ側での値チェック)を基本戦術とする。
2. どうしても防ぎきれないシステム側の数値溢れやアンダーフロー、予期せぬデータ破損に対しては `ON ERROR` や `ON ZERODIVIDE` をセーフティネットとして張る。
3. コンパイル時には必ず `OPTIMIZE(2)` などの最適化オプションをかけ、FPUのパイプライン処理が最大限活きるコードになっているかをシストレ(JCLのリスト出力)で確認する。

識別子の命名規則が自由奔放で、一見するととっつきにくいPL/Iだが、裏で動いているハードウェアの仕様(FPUと例外機構)と対話できるようになれば、これほど信頼性が高く、タフな言語はない。
今日の話を胸に刻んで、次のバッチ改修では無駄なABENDを起こさない堅牢なコードを書いてくれよ。期待しているぞ。

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