メインフレームからJavaへ:PL/Iの「数値精度」を巡る深淵とマイグレーションの罠
メインフレームの現場で何十年と生き抜いてきた諸君、お疲れ様。
今、君たちの前には「レガシー資産のJava化」という巨大な壁が立ちはだかっているはずだ。PL/Iの堅牢なコードをJavaに書き換える際、多くの若手が「単なる型の置換」だと軽く見て、本番稼働後に数値の誤差で大炎上するケースを何度も見てきた。
今日は、PL/Iにおける`FIXED DECIMAL`と`PICTURE`属性、そしてJavaへの移行時に必ず直面する「メモリレイアウトと精度の呪縛」について、現場の知見を共有しよう。
—
1. PL/Iが抱える「10進演算」の暗黙の了解
PL/Iの`FIXED DECIMAL`は、IBM汎用機のハードウェアが持つ10進演算命令を直接叩くために最適化されている。一方、Javaの`BigDecimal`はオブジェクトだ。この「ハードウェア直結の効率」と「オブジェクトの柔軟性」の差を埋めるのが、今回の最大の関門になる。
特に、`PIC ‘S9(7)V99’`のような定義は、COBOL屋だけでなくPL/I屋にとっても基本中の基本だが、これをJavaへ移すとき、単に`double`で受けるのは万死に値する。浮動小数点誤差で、バッチの合計金額が1円合わないといった事態は、銀行勘定系では死を意味するからだ。
実践的なPL/Iコード例:精度を意識した記述
まずは、私たちが日々触れているPL/Iの標準的な定義を再確認しよう。
/ ————————————————————- /
/ 顧客勘定データ構造の定義例 /
/ FIXED DECIMALは内部的にパック10進数として保持される /
/ ————————————————————- /
DCL 1 ACCOUNT_RECORD,
5 ACCOUNT_ID CHAR(10), / 口座番号 /
5 BALANCE FIXED DEC(15, 2), / 残高: 整数13桁+小数2桁 /
5 STATUS_CODE CHAR(1) INIT(‘A’); / 状態フラグ /
/ VSAMファイル更新用のプロシージャ /
UPDATE_BALANCE: PROC(IN_AMT) OPTIONS(MAIN);
DCL IN_AMT FIXED DEC(15, 2);
/ ONユニットによるエラーハンドリングの基本 /
ON CONVERSION BEGIN;
PUT SKIP LIST(‘数値変換エラー発生:データ型不一致’);
SIGNAL ERROR;
END;
/ 残高加算処理 /
BALANCE = BALANCE + IN_AMT;
/ 文字列出力時のPICTURE変換(編集) /
DCL EDIT_BALANCE CHAR(18);
EDIT_BALANCE = BALANCE; / 自動的に編集パターンが適用される /
END UPDATE_BALANCE;
—
2. Javaマイグレーションにおける3つの落とし穴
PL/IからJavaへ移行する際、以下の3点について「思考の切り替え」が必要だ。
① スケールと精度の定義維持
PL/Iの`FIXED DEC(15, 2)`は、メモリ上で正確に9バイトを占有し、10進数として扱われる。これを`BigDecimal`にする際は、必ず`MathContext`や`RoundingMode`を固定して扱うこと。Javaのデフォルト動作に任せると、計算のたびに丸め誤差が蓄積する。
② メモリレイアウトとVSAM
PL/Iの構造体は、メモリ上で隙間なく詰め込まれる。JavaのPOJO(Plain Old Java Object)は、JVMによってメモリ配置が最適化されるため、構造がそのままバイナリで一致することはない。VSAMファイルを直接読み込む際、`COPYBOOK`からのマッピングはバイト配列レベルで厳密に制御しないと、数値フィールドの読み込みで予期せぬオフセットずれが発生する。
③ ONユニットの制御フロー
PL/Iの`ON CONVERSION`や`ON ENDFILE`は、プログラムの処理フローから「例外」を切り離す強力な仕組みだ。Javaではこれを`try-catch`ブロックで書き直すことになるが、PL/Iの`SIGNAL`による意図的なエラー発生とフロー制御を、Javaで適切にリファクタリングしないと、後から追跡不可能なスパゲッティコードが完成する。
—
3. ベテランからのアドバイス:どう戦うべきか
マイグレーションは「書き換え」ではなく「翻訳」だ。元のコードが持つ「意図」を汲み取らなければならない。
1. BUILTIN関数の挙動を理解せよ: `ABS`, `TRUNC`, `ROUND`といったPL/Iの組み込み関数は、計算精度を厳密に定義している。Javaの`Math`クラスや`BigDecimal`のメソッドが、それと同じ挙動を示すか、テストスイートで必ず検証すること。
2. ログとトレースを過剰なほどに: 移行直後は、PL/I側とJava側の両方で計算結果をログに出力し、突き合わせるフェーズを設けるのが「現場の常識」だ。
3. 「仕様書」を信じるな: 何十年も稼働しているメインフレームのプログラムには、仕様書に書かれていない「現場の修正(パッチ)」がソースコードの中にこそ眠っている。まずはPL/Iのソースを読み込み、挙動を把握すること。
結びに代えて
PL/Iは、型に対して非常に誠実な言語だ。その厳格さをJavaという現代的な言語で再構築することは、一見すると退屈な作業に見えるかもしれない。しかし、基幹システムの心臓部を担う数値を扱う以上、これほどやりがいのある仕事はない。
「なんとなく動く」コードではなく、「数学的に証明可能な」コードを書くこと。それが、メインフレームで鍛え抜かれた諸君に求められるエンジニアリングの極致だ。
次回の更新では、VSAMアクセスをJavaで再現する際の『アクセスパスの最適化とコネクションプール』について深掘りしようと思う。現場からは以上だ。また現場で会おう。
