【実務・中級編】FIXED BINARY(p,q)の精度管理と内部表現(2進数と10進数の変換コスト) – PL/Iの基本構文とデータ制御実践ガイド

おい、最近あがってきた勘定系バッチの修正プログラムを見たんだが……またやってしまったな。`FIXED BINARY(31)` と `FIXED DECIMAL(15,2)` を無造作に足し合わせるようなコードを書きやがって。テスト環境では動くかもしれないが、本番の数千万件のトランザクションを回したとき、その隠れた「変換コスト」がどれだけCPU時間を食いつぶすか、想像したことがあるか?

メインフレームのPL/I開発において、データ型の選定と精度管理は、単なる「コンパイルエラーを防ぐお作法」じゃない。毎夜のバッチウィンドウを定時内に終わらせるための、死活問題なんだよ。

今日はな、PL/Iの `FIXED BINARY(p,q)` の内部表現の仕組みと、そこに潜む2進数・10進数の変換ペナルティについて、現場の泥臭い実態を踏まえて徹底的に叩き込んでやる。耳の穴かっぽじってよく聴け。

1. なぜ `FIXED BINARY` なのか? ―― 10進数と2進数の宿命

PL/Iには、数値を扱うための代表的なデータ型として `FIXED DECIMAL(パック十進数 / COMP-3)` と `FIXED BINARY(2進固定小数点 / COMP)` がある。

COBOL上がりのプログラマによくありがちなんだが、「金額だから」「帳票に出すから」という理由で、なんでもかんでも `FIXED DECIMAL` を使っていなか? あるいは逆に、ループカウンタだからと深く考えずに `FIXED BINARY` を使っていなか?

ここが最初の分かれ道だ。

  • `FIXED DECIMAL(p, q)`: 10進数の1桁を4ビット(半バイト)で表現し、末尾に符号(S, C, Dなど)を持つ。人間が読む分には直感的だが、CPUが直接算術演算を行うことはできない。演算のたびに、内部的にレジスタへ展開して計算する。
  • `FIXED BINARY(p, q)`: 符号付き2進整数として、IBM System/zの汎用レジスタ(GPR)上でそのまま直接演算できる。つまり、CPUのネイティブなデータ型だ。

基幹システムの心臓部であるバッチ処理において、ループ制御変数、配列のインデックス、高速な集計フラグなどは、圧倒的に `FIXED BINARY` が有利だ。なぜなら、余計なパッキング・アンパッキングの命令をCPUに踏ませずに済むからな。

2. 精度 `(p, q)` の設計ミスが招く「暗黙の型変換」地獄

だが、この `FIXED BINARY(p, q)`、使い所を誤るとコンパイラが裏でこっそり重たい変換ルーチンを生成しやがる。これが「変換コスト(オーバーヘッド)」の正体だ。

特に気をつけなければならないのが、異なる属性を持つデータ同士の演算や、VSAMファイル・レコード(不安全な型混入)との入出力時だ。

精度 `p` と `q` の基本ルール

  • `p` (Precision): 全体の有効桁数(2進数の場合はビット数ではなく、10進換算の桁数、またはコンパイラオプションに依存するが基本はビット長。Enterprise PL/Iでは `FIXED BINARY(31)` で31ビット、すなわち符号なしなら4GB強、符号つきなら約±21億まで扱える)。
  • `q` (Scaling factor): 小数点以下の桁数(2進数の場合は、小数点位置を右から何ビット目に置くか)。

もし、コード内で以下のような混算をしたとする。

1
DCL W_AMOUNT_BIN FIXED BINARY(31,0);
DCL W_RATE_DEC FIXED DECIMAL(5,4);
DCL W_RESULT FIXED BINARY(31,2);

W_RESULT = W_AMOUNT_BIN W_RATE_DEC;

一見、何気ない掛け算に見えるだろ? だが、コンパイラはこの瞬間、以下の処理を裏で強制される。
1. `FIXED BINARY` である `W_AMOUNT_BIN` を、一度 10進数(`FIXED DECIMAL`)へ変換する。
2. もう一方の `W_RATE_DEC` との間で、10進数としての乗算を行う。
3. その結果を、代入先の `W_RESULT` の属性(`FIXED BINARY(31,2)`)に合わせるために、再び2進数へと逆変換する。

……おいおい、バカバカしいと思わないか? CPUにとってネイティブなはずの `FIXED BINARY` 同士の演算に持ち込むために、わざわざ遅い10進数演算のルーチンを呼び出し、終わったら元に戻しているんだ。これが数百万件のループ内で発生してみろ。CPU使用率が一気に跳ね上がり、運用管理課から「夜間バッチのCPU時間を超過している」と怒りの内線が飛んでくることになる。

3. 実践コード:安全かつ高速な精度管理とビルトイン関数の活用

百聞は一見にしかずだ。実際のバッチプログラムを想定したサンプルコードを見せてやろう。
VSAMファイル(KSDS)からレコードを読み込み、内部で `FIXED BINARY` を駆使して高速に集計を行い、ONユニットで例外を綺麗に捕捉する構成だ。

1
——————————————————————-;

  • 顧客別売上集計バッチプログラム
  • ターゲット: FIXED BINARY(31,0) による高速な数値演算と精度管理

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

— ファイル定義 (VSAM KSDS) –;
DCL CUST_FILE FILE RECORD SEQUENTIAL
ENV(INPUT)
KEYED;

— レコードレイアウト(外部データはFIXED DECIMALが主流) –;
DCL 1 CUST_REC,
5 CR_CUST_ID CHAR(8),
5 CR_SALES_AMT FIXED DECIMAL(11,2), — 10進数(外部表現) –;
5 CR_QTY FIXED BINARY(15,0); — 2進数(数量)

— 内部演算用ワーク(CPU効率を考慮しFIXED BINARYに統一) –;
DCL W_TOTAL_SALES FIXED BINARY(31,2) INIT(0); — 内部計算用(2進小数点) –;
DCL W_UNIT_PRICE FIXED BINARY(31,2) INIT(1500);
DCL W_CALC_WK FIXED BINARY(31,0);
DCL W_EOF_FLG BIT(1) INIT(‘0’B);

— 異常系制御(ONユニット) –;
ON ENDFILE(CUST_FILE) W_EOF_FLG = ‘1’B;

ON CONVERSION
BEGIN;
DISPLAY(‘【警告】データ変換エラーを検出しす。ブランク置換します。’);
— 現場の応急処置としてゼロ置換やログ出力を行う –;
END;

— 処理開始 –;
OPEN FILE(CUST_FILE) INPUT;

READ FILE(CUST_FILE) INTO(CUST_REC);

DO WHILE(^W_EOF_FLG);

———————————————————–;

  • 【重要】外部のFIXED DECIMALを内部のFIXED BINARYへ明示的に変換
  • CONVERTビルトイン関数やアサインメント時に意図した型に落とす

———————————————————–;
W_CALC_WK = BIN(CR_SALES_AMT, 31, 0); — 明示的キャストで無駄なペナルティを防ぐ –;

— 2進数同士の高速な演算 –;
IF CR_QTY > 0 THEN
DO;
W_TOTAL_SALES = W_TOTAL_SALES + (W_CALC_WK W_UNIT_PRICE);
END;

READ FILE(CUST_FILE) INTO(CUST_REC);
END;

— 結果出力 –;
DISPLAY(‘総売上金額(2進演算結果): ‘ || EDIT(W_TOTAL_SALES, ‘ZZZ,ZZZ,ZZ9.92’));

CLOSE FILE(CUST_FILE);

RETURN;
END CUST_SUMMARY;

コードの解説と実務のポイント

1. 明示的な型変換(`BIN` ビルトイン関数)の活用
コード内で `BIN(CR_SALES_AMT, 31, 0)` と記述している部分に注目しろ。コンパイラ任せに暗黙の型変換をさせると、どのようなコードが生成されるか予測がつかない。あらかじめ `BIN` や `DEC` ビルトイン関数を使って「ここで型を変換する」とプログラマが明示することで、コンパイル時に最適な機械語命令が選択される。
2. ON CONVERSION による堅牢性
VSAMや外部ファイルからの入力時、半角スペースやゴミデータが混入していて数値変換エラー(`CONVERSION`)が発生することは実務では日常茶飯事だ。ONユニットを適切に配置し、システムが異常終了するのを防ぎつつ、適切なロギングを行うこと。これがプロのアーキテクトの仕事だ。
3. 演算精度の桁あふれ(Overflow)への配慮
`FIXED BINARY(31,0)` の最大値は約21億だ。もし日次バッチで合算する金額がこの数値を軽く超えることが分かっているなら、ケチらずに `FIXED BINARY(63,0)`(ダブルワード精度)を定義しろ。オーバーフローが発生した瞬間にプログラムが異常終了(ABEND: S37Dなど)し、夜中の3時にオペレータから叩き起こされるハメになるぞ。

4. デバッグとチューニングのコツ:コンパイラリストの読み方

最後に、先輩からの実践的なアドバイスだ。
自分が書いたPL/Iコードが、本当に無駄な変換ルーチンを吐いていないか確認したくなったら、コンパイル時のリスト出力(LISTオプションやOFFSETオプション)を必ず確認しろ。

コンパイラリストのシグネチャの中に、見慣れないサブルーチン呼び出しや、`CVB`(Convert to Binary)、`CVD`(Convert to Decimal)といったアセンブラ命令が不自然に多用されている箇所があれば、それは「データ型のミスマッチによる無駄なペナルティ」が発生している証拠だ。

  • ループの内側で型を混ぜるな。
  • ファイル入出力のバッファ定義(外部表現)と、ワークエリア(内部演算表現)を厳密に分離せよ。
  • 精度 `p` と `q` は、将来のデータ増加を見越して必要十分なサイズ(基本は `FIXED BINARY(31)` または `(63)`)を設計せよ。

この基本を体に叩き込んでおけば、どんなに古いレガシーシステムの移行案件であっても、パフォーマンスで足をすくわれることは絶対にない。さあ、手を動かして自分の担当モジュールの属性定義を見直してみろ!

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