【実務・中級編】固定小数点演算におけるオーバーフロー条件 – PL/Iの基本構文とデータ制御実践ガイド

おい、最近ジョブが突然「ABEND (S0C7じゃないぞ!)」で落ちて頭を抱えていないか?

「夜間バッチの途中でおかしなデータが流れてきて、システムが止まった」
「VSAMから読み込んだマスタの金額と、計算結果の桁数が合わなくてトラブった」

メインフレームの現場じゃ、こういうトラブルは日常茶飯事だ。特にPL/Iで書かれた基幹システムの心臓部を触るとき、避けて通れないのが固定小数点数(FIXED BINARY / DECIMAL)の精度と、それに伴うオーバーフロー(SIZE条件)の制御だ。

COBOLならコンパイルオプションや緩い切り捨てで何となく通ってしまうような計算も、IBMのPL/Iコンパイラは非常に厳格だ。今回は、この「SIZE条件」と「ON SIZEユニット」の正しい手懐け方について、現場のノウハウを交えて徹底的に叩き込んでやる。しっかりついてこい。

—

1. なぜPL/Iの固定小数点演算は恐ろしいのか?

PL/Iのデータ型、特に `FIXED DECIMAL`(パック十進数:COMP-3相当)や `FIXED BINARY`(二進固定小数点数:COMP/COMP-4相当)は、宣言された「精度(precision)」の枠内でしか生きられない。

例えば、以下のように宣言したとする。

1
DCL WK-AMT FIXED DECIMAL(5, 2); / 整数部3桁、小数部2桁 (-999.99 ~ +999.99) /
DCL WK-PRC FIXED DECIMAL(3, 0); / 整数部3桁、小数部0桁 (-999 ~ +999) /

この `WK-AMT` に、もし間違って `1000.00` という値が入ったり、乗算の結果 `1200.50` が代入されようとした時、何が起きるか?
COBOLであれば高位桁落ちは暗黙的に切り捨てられるか、設定次第でスルーされることもある。しかし、PL/Iのデフォルト(またはコンパイルオプション)では、この「定義された桁数を超える溢れ」を検知するために `SIZE` 条件(Condition) が発生する。

もし `ON SIZE` でトラップを仕掛けていなかった場合、プログラムは容赦なく IBM0201S などのソフトウェア割り込み(または異常終了) を引き起こして夜間バッチを止めてくれる。これがメインフレームの現場で恐れられている「SIZEオーバーフロー」の正体だ。

—

2. SIZE条件とON SIZEユニットの正しい制御フロー

PL/Iには、例外処理をエレガントに行うための ONユニット という強力な仕組みがある。これを使えば、オーバーフローが発生した瞬間にプログラムを強制終了させるのではなく、救済処理(ログ出力、代替値のセット、エラーフラグの立て方など)を行うことができる。

ON SIZEユニットの構文とスコープ

1
ON SIZE
BEGIN;
/ オーバーフロー発生時の救済・ログ出力処理 /
ERROR-FLAG = ‘1’;
CALC-RESULT = 0; / 安全な初期値を退避 /
END;

ここでベテランとして一つ重要な注意点がある。ONユニットの有効範囲(スコープ) だ。
ONユニットは、それを記述したブロック(あるいは動的に呼び出される下位プロシージャ)の実行中有効だが、グローバルに影響を与えるため、意図しない箇所で発火しないようスコープの管理には細心の注意を払う必要がある。

また、すべての演算で `ON SIZE` を書くのはナンセンスだ。本当に桁あふれの危険性がある高精度計算(金融系の金利計算や、VSAMからの生データアンパック後の最初の算術演算など)に絞って適用するのが、洗練されたアーキテクトのやり方だ。

—

3. 実践:VSAM入出力と算術演算におけるSIZE制御プログラム

百聞は一見にしかずだ。実際のメインフレーム開発現場を想定した、実用的なPL/Iのサンプルコードを見せよう。

このプログラムは、VSAM(KSDS)から売上レコードを読み込み、数量と単価を掛け合わせて金額を算出し、その過程で発生しうるSIZEオーバーフローを `ON SIZE` でトラップして安全に処理するバッチ処理の骨組みだ。

1
—————————————————————-

  • プログラム名: SIZESPL1
  • 概要 : 固定小数点演算におけるSIZE条件制御のサンプル

—————————————————————-
SIZESPL1: PROC OPTIONS(MAIN);

/ — データ定義 — /
DCL 1 SALES-REC,
15 CUST-ID CHAR(5), / 顧客ID /
15 QTY FIXED BIN(15), / 数量 (バイナリ) /
15 UNIT-PRICE FIXED DEC(7,2); / 単価 (7桁、小数2桁) /

DCL TOT-AMT FIXED DEC(9,2)
INIT(0); / 合計金額 (9桁、小数2)/

DCL ERR-FLG CHAR(1) INIT(‘0’); / エラーフラグ /
DCL EOF-FLG CHAR(1) INIT(‘0’); / 読込終了フラグ /

/ — VSAMファイル定義 (例: 顧客売上ファイル) — /
DCL SALES-FILE FILE RECORD SEQUENTIAL
ENVIRONMENT(BUFFERS(2));

/ — 異常終了対策:ファイルオープンエラー時の処理 — /
ON UNDEFINEDFILE(SALES-FILE)
BEGIN;
PUT SKIP LIST(‘ ERROR: SALES-FILE OPEN FAILED. ‘);
SIGNAL ERROR;
END;

OPEN FILE(SALES-FILE) INPUT;

/ — メイン処理ループ — /
DO WHILE (EOF-FLG = ‘0’);

READ FILE(SALES-FILE) INTO(SALES-REC);
IF / 読込 End-of-File 判定ロジック(疑似) / (/ EOFなら /) THEN
DO;
EOF-FLG = ‘1’;
LEAVE;
END;

/ ================================================== /
/ SIZE条件(オーバーフロー)のトラップ定義開始 /
/ ================================================== /
ON SIZE
BEGIN;
PUT SKIP EDIT(‘WARNING: SIZE OVERFLOW DETECTED. CUST=’, CUST-ID)
(A, A);
ERR-FLG = ‘1’;
TOT-AMT = 9999999.99; / オーバーフロー時の上限値をフォールバック /
END;

/ 算術演算の実行:数量(FIXED BIN) × 単価(FIXED DEC) /
/ この乗算結果が TOT-AMT の精度(9,2)を超える場合、SIZE条件発火 /
BEGIN;
DCL WK-CALC FIXED DEC(9,2);

WK-CALC = QTY UNIT-PRICE;
TOT-AMT = TOT-AMT + WK-CALC;
END;

/ 一度設定したONユニットをこのブロック終了時に無効化・リセットする場合の考慮 /
REVERT SIZE;

END;

CLOSE FILE(SALES-FILE);

PUT SKIP LIST(‘BATCH PROCESSING COMPLETED. ERR-FLG=’, ERR-FLG);
RETURN;

END SIZESPL1;

—

4. 現場のエンジニアへ贈るデバッグとコーディングの鉄則

最後に、現場で数々の修羅場をくぐってきた私から、君たち後輩へのアドバイスをいくつか残しておこう。

1. ビルトイン関数を賢く使え (`MULTIPLY`, `ADD` など)
PL/Iでは、演算時の精度を明示的にコントロールするために `MULTIPLY(A, B, p, q)` や `ADD(A, B, p, q)` といったビルトイン関数が用意されている。これらを使うことで、コンパイラが勝手に決める中間項の精度ではなく、プログラマが意図した精度で演算を行わせることができる。桁あふれを未然に防ぐ最大の武器だ。
2. コンパイルオプション(STGIO / SIZE / CHECK)の確認
マイグレーションやリプレイス案件で「なぜか本番機だけ突然ABENDする」という現象の多くは、コンパイラオプションの差異(例: `LIMIT` や `SIZE` に関するオプション)に起因する。移行前後のコンパイルJCLは一文字たりとも見落とすな。
3. 「起きるはずがない」を疑う
「マスタの単価は高々3桁だからオーバーフローしない」という思い込みが、数年後のデータ増大や仕様変更時にシステムをクラッシュさせる。`ON SIZE` を適切に配置し、万が一のデータ異常時にもログを残して安全にバイパスする設計(ディフェンシブ・プログラミング)を貫いてほしい。

メインフレームのコードは嘘をつかない。書いた通りのロジックで忠実に動く。だからこそ、言語仕様の裏側まで理解したアーキテクトであれ。次の現場での健闘を祈る!

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