おい、最近JOINした若手が「なぜかこの数値項目の加算で突然ABEND(S0C7じゃないぞ!)するんです」と青い顔をして駆け込んできたんだ。画面を見ると、VSAMから読み込んだレコードの数値を処理するごく普通のPL/Iプログラム。原因を調べたら、やっぱり`FIXED BINARY`の精度(P, Q)の罠に綺麗にハマっていた。
メインフレームの現場で長く生きていると、「たかが数値の定義だろう」と甘く見たせいで、本番の夜間バッチが盛大に吹き飛ぶ瞬間を何度も目撃してきた。COBOLなら`COMP`やペンタ・コンピュートの桁数であたふたするところだが、PL/Iの世界にはPL/I独自の、そして非常にシビアな「2進整数(FIXED BINARY)」のルールが存在する。
今日は、この`FIXED BINARY`の内部表現、精度指定のメカニズム、そして現場で絶対に知っておくべきオーバーフローの罠について、実務の現場目線で徹底的に叩き込んでやろう。
—
1. 予約語を持たないPL/Iの懐の深さと「識別子」の恐怖
まず大前提として、PL/IにはCOBOLのような「厳格な予約語(Reserved Words)」という概念がほとんどない。`IF`や`DO`といったキーワードですら、文脈によっては単なる変数名(識別子)として定義できてしまう。
この「何でも変数にできる」という言語仕様の懐の深さは、時として凶器に変わる。
例えば、以下のようなコードを書くバカはいないと思うが……いや、レガシーなソースでは稀にこれに近い惨劇を見る。
1
DECLARE FIXED FIXED BINARY(31);
変数名に `FIXED` を使ってしまっている。コンパイラは文脈から判断してくれるが、コードを読む人間の脳みそは確実に破壊される。識別子は最大31文字、英字で始まり、英数字とアンダースコア(`_`)が使える。この基本を守りつつ、キーワードと見間違うような命名は絶対に避けるべきだ。これが保守性を守る第一歩だ。
—
2. `FIXED BINARY(P, Q)` の内部表現と「2の補数」の仕組み
さて本題の `FIXED BINARY` だ。
PL/Iの固定小数点2進数(`FIXED BINARY`)は、IBMメインフレームのハードウェア(System z)が最も得意とする演算フォーマット、すなわち「2の補数(Two’s Complement)」形式でメモリ上に保持される。
ここで重要になるのが、宣言時に指定する 精度(P, Q) だ。
- P(総ビット数): 符号ビットを含む、保持する総ビット数。
- Q(スケーリングファクター): 小数点以下のビット数。
もし `Q = 0` ならば、それは純粋な「2進整数」を意味する。
厄介なのは、この Pの値によってコンパイラが割り当てる実際の物理的なストレージサイズ(バイト数)が自動的に決まる という点だ。
- `P` が 1 ~ 15: 半ワード(2バイト / 16ビット)
- `P` が 16 ~ 31: フルワード(4バイト / 32ビット)
- `P` が 32 ~ 63: ダブルワード(8バイト / 64ビット)
よくある勘違いが、「`FIXED BINARY(15)` と書けば2バイト、`31` と書けば4バイト」という事実を忘れて、やたらと大きな精度を雑につけてしまうことだ。無駄に大きなストレージを使うだけでなく、演算の効率やレコードレイアウト(構造体)のパディングにも影響してくる。
—
3. コンパイラオプション(DEFAULT)による暗黙の罠
さらに現場で恐ろしいのは、精度を省略して単に `DECLARE A FIXED BINARY;` と書いた場合だ。
この場合、コンパイル時に指定されているオプション(`DEFAULT` または `DEFS(STD)` など)によって、デフォルトの精度が勝手に補完される。
ある処理系では `FIXED BINARY(15)` になり、別のメインフレーム環境や移行先のコンパイラでは `FIXED BINARY(31)` になる、なんてことが起こり得る。「動いていたプログラムを別環境で再コンパイルしたら結果がおかしくなった」というトラブルの多くは、このデフォルト精度の違いに起因している。
実務のコーディング標準としては、面倒臭がらずに 必ず明示的に精度(例: `(31,0)` や `(15,0)`)を書く。これがプロの流儀だ。
—
4. 実践:VSAM入出力とONユニットによるオーバーフロー制御
では、ここまでの知識を踏まえて、実際のメインフレーム環境(VSAMファイルからの読み込み、加算処理、そしてオーバーフロー発生時のONユニットによるトラップ)を想定した実用的なPL/Iプログラムを見てみよう。
大文字ベースで記述された、すぐにでも現場のバッチに組み込めるコードだ。
1
—————————————————————-
- 模块名: VFIXBIN1
- 概要 : VSAM(KSDS)から読み込んだFIXED BINARYデータの演算と
- オーバーフロー制御のサンプルプログラム
—————————————————————-
VFIXBIN1: PROC OPTIONS(MAIN);
/ VSAMファイルのレコード定義(KSDSを想定) /
DECLARE 1 WS_VSAM_REC,
5 VS_KEY CHAR(5), / キー領域 /
5 VS_AMT_RAW FIXED BIN(31) / 金額(2進整数)/
;
/ 作業用変数:精度不足によるオーバーフローを故意に起こしやすい定義 /
DECLARE WK_BASE_AMT FIXED BIN(15) INITIAL(0);
DECLARE WK_TOTAL_AMT FIXED BIN(31) INITIAL(0);
/ ファイル定義 /
DECLARE IN_FILE FILE RECORD INPUT
ENVIRONMENT(JSAM); / 実際はVSAM等に合わせる /
/ 終了フラグ /
DECLARE EOF_FLG CHAR(1) VALUE(‘OFF’);
/ ——————————————————– /
/ エラー制御(ONユニット):FIXEDOVERFLOW割り込みの捕捉 /
/ ——————————————————– /
ON FIXEDOVERFLOW
BEGIN;
DISPLAY(‘【警告】FIXED BINARYのオーバーフローを検知しました!’);
DISPLAY(‘該当キー: ‘ || VS_KEY);
DISPLAY(‘処理を継続しますが、値がラップアラウンドしています。’);
/ ここで異常終了させるか、エラー用退避エリアに送るかを制御 /
END;
/ ファイルオープン /
OPEN FILE(IN_FILE);
/ メインループ /
DO WHILE(EOF_FLG = ‘OFF’);
READ FILE(IN_FILE) INTO(WS_VSAM_REC);
IF / 簡易的なファイルEOF判定 / … THEN
EOF_FLG = ‘ON’;
ELSE
DO;
/ 内部関数(BUILTIN)を活用した絶対値取得やパディングチェック /
/ WK_BASE_AMTはFIXED BIN(15)のため、大きな値が入ると即座にOVF /
WK_BASE_AMT = VS_AMT_RAW; / ここで精度変換とサイズ縮小が発生 /
IF ABS(WK_BASE_AMT) > 30000 THEN
CALL 異常ログ出力処理(VS_KEY);
ELSE
WK_TOTAL_AMT = WK_TOTAL_AMT + FIXED(VS_AMT_RAW, 31, 0);
END;
END;
CLOSE FILE(IN_FILE);
RETURN;
異常ログ出力処理: PROC(P_KEY);
DECLARE P_KEY CHAR(5);
DISPLAY(‘異常データ検出キー: ‘ || P_KEY);
END 異常ログ出力処理;
END VFIXBIN1;
このコードのポイントと実務の急所
1. `WK_BASE_AMT FIXED BIN(15)` への代入の危険性
VSAM側から `FIXED BIN(31)` で読み込んだデータを、より精度の低い `FIXED BIN(15)` に代入している。この瞬間、上位側のビットが切り捨てられるか、値が範囲外であればハードウェア割り込みが発生する。
2. `ON FIXEDOVERFLOW` によるトラップ
PL/Iの強力な機能の一つが、この `ON` ユニットによる例外処理だ。COBOLではそのまま強制終了するか無言で上位桁落ちするかといった挙動になりがちな算術オーバーフローを、PL/Iではプログラマブルに捕捉できる。ただし、夜間バッチで毎レコードこれを踏むとログが溢れ返るので、あくまで「想定外の異常系」の安全弁として使うこと。
3. `FIXED` ビルトイン関数の活用
コード内にある `FIXED(VS_AMT_RAW, 31, 0)` のように、組み込み関数(BUILTIN)を使って明示的に精度をキャスト(再定義)するテクニックは、異なる精度間で演算を行う際の予期せぬ桁落ちを防ぐために極めて有効だ。
—
5. ベテランからのアドバイス:保守・マイグレーション時の心得
古いACOSやMSP、あるいはGS21といったメインフレームから、オープン系やクラウド、あるいは現代的なIBM Z上のコンパイラへソースを移行する際、この `FIXED BINARY` の扱いは最もバグの温床になりやすい。
- 「なんとなく動いているから触らない」は死刑宣告
前任者が適当に書いた `FIXED BINARY` の精度不足は、データ量が膨らんだ瞬間に突然牙をむく。
- 構造体(RECORDS)のバイナリ互換性
外部ファイルや他のサブシステムとバイナリ(生データ)でデータをやり取りする場合、精度の違い(15bitなのか31bitなのか)は構造体のオフセット(位置)を完全に狂わせる。マイグレーションの際は、`DECLARE` 文の精度指定を1文字たりとも見落としてはならない。
PL/Iは、言語仕様を正しく理解していればこれほど信頼性が高く、ハードウェアの性能を極限まで引き出せる言語はない。逆に、ルールを侮ると容赦なくシステムを沈める。
今日の解説を頭に叩き込んで、次のバッチ改修では一歩踏み込んだ安全なコードを書いてくれ期待しているぞ。
