【テクニカル・上級編】FIXED DECIMAL(p,q)のパック10進数形式と演算 – PL/Iの基本構文とデータ制御実践ガイド

メインフレームの心臓部:「FIXED DECIMAL」という名の深淵と、その継承

長年メインフレームの現場に身を置いていると、「なぜJavaに移行した途端に金額計算が合わなくなるのか」という嘆きを耳にする。答えは単純だ。彼らがPL/Iの`FIXED DECIMAL(p,q)`という、ハードウェアレベルで最適化された精緻な仕組みを、浮動小数点という「近似の海」に放り込んでしまったからだ。

今日は、PL/Iにおけるパック10進数(COMP-3)の深淵を覗き、マイグレーション時に我々アーキテクトが何に絶望し、何を救い出さねばならないのかを語ろう。

1. COMP-3の構造:メモリに刻まれた「10進数」の意志

PL/Iで定義される `DCL AMOUNT FIXED DEC(15, 2);`。これは単なる数値ではない。IBM z/Architectureにおいて、パック10進数は1バイトに2桁を詰め込み、末尾のニブル(4ビット)で符号を表現する。

/ 15桁のパック10進数(8バイト)のメモリイメージ /
/ 00 00 00 00 00 00 12 3C (123.00の場合) /
/ 最後の’C’は正符号。ここが’D’なら負、’F’なら符号なし(正として扱われる) /

この「符号ビット」が曲者だ。EBCDICコードと混在するようなレガシーインターフェースでは、符号なしの`’F’`が混入し、演算時に`S0C7`(データ例外)を吐くことは珍しくない。特にCICSオンラインで他システムからの電文を受け取る際、符号の整合性が取れていないデータを受け取った瞬間にプログラムは停止する。

教訓: 外部入力データに対しては、演算前に必ず `IF (AMOUNT < 0 OR AMOUNT >= 0) THEN` といった、コンパイラがパックデシマルのバリデーションを行うコードを強制的に走らせるのが、最も安上がりの保険だ。

2. 算術演算の罠:精度維持と変換コスト

PL/Iのコンパイラは優秀だ。だが、その優秀さが仇となることもある。
`FIXED DEC(5,2) FIXED DEC(5,2)` の結果が `FIXED DEC(10,4)` になるというルールは、基幹システムの演算において「オーバーフロー」を防ぐための自動拡張だが、これがループ内で多発すればCPUコストは跳ね上がる。

最適化の勘所

マイグレーション先がJavaであれば`BigDecimal`を使うことになるが、PL/Iで行われていた「演算ごとの内部的な精度調整」をすべて明示的に記述しなければ、中間結果の桁落ちで計算が狂う。

/ パフォーマンスと精度のトレードオフ /
DCL A FIXED DEC(9,2) INIT(100.00);
DCL B FIXED DEC(9,2) INIT(3.00);
DCL C FIXED DEC(9,2);

/ コンパイラは中間結果を保持するために内部的にスタックを使う /
/ 複雑な式は、敢えて中間変数に落とし込むことで解析が容易になる /
C = (A B) / 100;

特にDB2の埋め込みSQLで `DECIMAL` 型を扱う際、ホスト変数の定義とSQL側の型が一致していないと、実行時に暗黙の型変換(パックデシマルから浮動小数点への変換)が発生する。これが大規模バッチにおいて、CPU使用率を数%底上げする「見えないコスト」の正体だ。

3. アベンド(ABEND)解析:ダンプは嘘をつかない

`S0C7`が発生したとき、諸君はどうする?
多くのジュニアエンジニアはソースコードを眺めるが、真のアーキテクトはまずダンプの「PSW(プログラムステータスワード)」と「レジスタ」を見る。

パックデシマル演算でアベンドする場合、原因はほぼ間違いなく「符号の破壊」か「桁あふれによる無効な数値」だ。ポインタを用いてメモリを直接操作するような禁じ手(`BASED`変数でのオーバーレイ)を使っている場合、メモリの境界を越えてパック形式のデータが壊れているケースが多い。

/ 危険なポインタ操作の例 /
DCL PTR POINTER;
DCL MY_VAR CHAR(8) BASED(PTR);
/ ここでパックデシマルの領域を無理やり文字として操作すると… /
/ 符号領域が壊れ、後続の計算でS0C7の爆弾が炸裂する /

4. マイグレーションへの提言:移行先で「再現」させる勇気

JavaやC#への移行において、PL/Iの「自動的な符号補正」や「桁数制限による切り捨て処理」をフレームワーク側に隠蔽してはならない。

1. データ型の定義: 移行先言語でも`BigDecimal`等の固定精度演算クラスを使い、桁数とスケールを明示的に定義する。
2. バリデーションの共通化: `COMP-3`の符号ビットチェックに相当するロジックを、汎用ライブラリとして提供する。
3. 性能評価: パックデシマル1回の加算と、オブジェクト生成を伴う`BigDecimal`の加算では、速度差が桁違いであることを理解させる。

我々の仕事は、単なるコードの書き換えではない。メインフレームが40年かけて培ってきた「計算の正確性」という哲学を、現代の言語の上で再実装することだ。

もし諸君が、今まさに基幹システムのソースコードと睨めっこしているのなら、一つだけ覚えておいてほしい。
「コンピュータは、君が書いたコードの通りに動くのではない。君が定義した『型』の通りに動くのだ」と。

PL/Iの`FIXED DECIMAL`は、その「型」に対する厳格さの結晶である。それを軽んじる設計は、必ず数年後の夜中に、システムダウンという形で報復を受けることになるだろう。

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