おい、最近のモダナイゼーション案件の進捗はどうだ?
「PL/Iで動いている勘定系や基幹バッチを、そっくりそのままJava(Spring Bootあたりか?)に置き換える」なんて話を、ここ数年で耳にタコができるほど聞いてきたことだろう。
だがな、甘い汁ばかり吸おうとして足元をすくわれる現場を、俺は嫌というほど見てきた。
特に、データ型のマッピング。ここで手を抜くと、本番稼働した瞬間に金額が合わなくなったり、夜間バッチが謎の`NumberFormatException`で盛大にクラッシュしたりする。地獄絵図だぞ。
今日は、PL/Iの根幹をなす「FIXED DECIMAL」や「ピクチャ型」を、Javaの「BigDecimal」や「String」へ安全に、かつ狂いなく移行するための実践的なノウハウを叩き込んでやる。耳の穴をかっぽじってよく聞け。
—
なぜPL/IからJavaへの移行で「お金の計算」が狂うのか?
PL/Iの最大の特徴の一つは、ハードウェアの十進演算命令を直接叩ける、極めて厳密な固定小数点演算(`FIXED DECIMAL`)だ。
たとえば、金融システムの口座残高や利息計算で以下のような定義を見たことがあるはずだ。
1
DCL WK-BALANCE FIXED DEC(11, 2);
これは「全体で11桁、そのうち小数点以下が2桁」のゾーン十進数(あるいはパック十進数)を意味する。
これを何も考えずにJavaのプリ型(`double`や`float`)にマッピングした瞬間、お前のシステムは即座にゴミクズと化す。浮動小数点演算特有の丸め誤差($0.1 + 0.2 \neq 0.3$のアレだ)により、1円のズレも許されない基幹システムで大チョンボをやらかすことになるからだ。
だからこそ、Java側では`java.math.BigDecimal`を使うのが絶対の正義なのだが……ここからが罠の始まりだ。
—
1. `FIXED DECIMAL` と `BigDecimal` のスケール(小数点以下桁数)の罠
PL/IとJavaの間でデータを受け渡す際(例えば、VSAMファイルを読み込んだフラットファイルをJSONやDB経由でJavaに渡すようなインターフェース設計時)、スケールの扱いにズレが生じると致命傷になる。
以下のPL/Iのコード例を見てみよう。ここでは、実務でよくあるVSAMのレコードレイアウトを想定し、数値演算とピクチャ編集を行っている。
1
/ —————————————————————- /
/ プログラム名: MIGR01 – データ型変換と演算のサンプル /
/ —————————————————————- /
MIGR01: PROC OPTIONS(MAIN);
/ — 変数宣言 — /
DCL IN-RECORD CHAR(50); / VSAM入力レコード /
/ 厳密な固定小数点数(全体11桁、小数2桁) /
DCL WK-PRICE FIXED DEC(9, 2) INIT(0);
DCL WK-QTY FIXED DEC(5, 0) INIT(0);
DCL WK-TOTAL-AMT FIXED DEC(11, 2) INIT(0);
/ 編集用ピクチャ型(印字・帳票出力用) /
DCL PRT-TOTAL-AMT PIC ‘ZZZ,ZZZ,ZZ9.99’;
/ — 演算処理 — /
WK-PRICE = 12345.50;
WK-QTY = 10;
/ 乗算処理:PL/Iのコンパイラは自動的に精度を拡張する /
WK-TOTAL-AMT = WK-PRICE WK-QTY;
/ ピクチャ型への転記(編集) /
PRT-TOTAL-AMT = WK-TOTAL-AMT;
PUT SKIP LIST (‘計算結果(DEC): ‘ || WK-TOTAL-AMT);
PUT SKIP LIST (‘編集結果(PIC): ‘ || PRT-TOTAL-AMT);
END MIGR01;
Java側でのマッピングにおける注意点
この `WK-TOTAL-AMT`(`FIXED DEC(11, 2)`)をJavaの `BigDecimal` に受け渡すとき、以下の点に気をつけなければならない。
1. スケール(Scale)の明示的設定
Java側で値を生成、あるいはDBからマッピングする際、`setScale(2, RoundingMode.HALF_UP)` などを明示的にかけないと、PL/I側が想定していた小数点以下の桁落ちや、予期せぬ丸めモードの不一致が発生する。
2. 有効桁数(Precision)のオーバーフロー
PL/Iの `FIXED DEC(11, 2)` は、整数部9桁・小数部2桁を保証する。Java側で `BigDecimal` を扱う際にも、必要に応じて `MathContext` を用いて桁数制限をエミュレートしないと、マイグレーション後に「PL/Iでは通った桁あふれ値が、Java側でしれっと保持されてしまう」というデータ汚染が起きる。
—
2. ピクチャ型(`PIC`)と `String` / 編集の罠
もう一つの大きな地雷が、PL/Iのピクチャ型(Picture Clause)だ。
PL/Iでは、数値でありながらカンマやドット、CR/DB(クレジット/デビット)などを埋め込んだ文字列として扱える強力なピクチャ機能がある。
例えば、`PIC ‘-$$,$$$,$$9.99’` のような定義だ。
これをJavaに移行する際、「見た目が文字列っぽいから」という理由で、安易にJavaの `String` 型としてDBに格納したり、画面表示用ロジックにそのまま流し込んだりすると、後で痛い目を見る。
現場でよくある失敗パターン
- 計算への流用: ピクチャ変数をそのまま別の数値項目の計算に巻き込んでしまい、コンパイルエラー(あるいは暗黙の型変換によるゴミデータの発生)に気づかない。
- 符号(Sign)の扱い: PL/Iのピクチャで `CR` や `DB`、あるいはマイナス記号が先行する形式(Trailing/Leading Sign)の場合、Javaの標準的な数値フォーマッタ(`DecimalFormat`)のパターンと解釈が微妙に異なり、負数のパースに失敗して `ParseException` の嵐になる。
正しいアプローチ
マイグレーションの基本原則として、「データ保持レイヤーでは純粋な数値(`BigDecimal`)を持ち、画面出力や帳票出力の直前のプレゼンテーション層で初めてピクチャ(フォーマット)を適用する」という設計思想に切り替えなければならない。
Javaでこれを実装する場合のイメージはこうだ:
// Javaでの安全な金額フォーマットの例(PL/Iの PIC ‘ZZZ,ZZZ,ZZ9.99’ 相当)
import java.math.BigDecimal;
import java.text.DecimalFormat;
public class AmountFormatter {
public static String formatToPl1Style(BigDecimal amount) {
// PL/Iのピクチャ定義に対応するDecimalFormatパターン
DecimalFormat df = new DecimalFormat(“
, #,##0.00″);
return df.format(amount);
}
}
—
3. ONユニットと例外処理の精神を引き継げ
PL/Iの堅牢性を支えている隠れた立役者が、このONユニット(例外・割込み処理機構)だ。
例えば、数値演算でオーバーフローが起きたときや、ファイル読み込みでデータが壊れていたときの挙動を制御するために、現場のコードにはこういった記述が散りばめられているはずだ。
1
/ 演算オーバーフロー時のトラップ /
ON OVERFLOW BEGIN;
PUT SKIP LIST (‘ 致命的エラー: 数値オーバーフローが発生しました ‘);
/ 異常終了コードの設定などのリカバリ処理 /
SIGNAL ERROR;
END;
Javaに移行する際、この「PL/Iが持っていた厳格な例外ハンドリングの思想」をごっそり忘れて、単なる `try-catch (Exception e)` で握り潰したり、ログだけ吐いて処理を続行したりするコードを書く奴が後を絶たない。
基幹システムにおいて、データ不整合を見逃すことは、誤った金額で請求書を飛ばすことと同義だ。
Javaへ移行するにあたっても、カスタム例外(例: `DataPrecisionOverflowException` など)を定義し、不正なデータや精度落ちを検知した瞬間にバッチを安全に異常終了させる仕組み(スプリング・バッチのチャンク処理におけるリスナーなど)を必ず作り込むこと。
—
まとめ:レガシーの「意図」を読み解け
PL/IからJavaへのマイグレーションは、単なる「構文の置き換え(トランスレーション)」ではない。
そのコードが「なぜそのデータ型を選び、なぜその精度で計算し、なぜそこで例外を捕捉しているのか」という、先人たちの設計思想(ビジネスロジックの意図)を現代の言語に翻訳する作業なのだ。
- 金額計算には絶対に `BigDecimal` を使え(`double` は厳禁)。
- スケールと丸めモード(RoundingMode)の仕様を日米(PL/IコンパイラとJavaランタイム)で一致させろ。
- ピクチャ型はデータ型ではなく「表示形式」として分離せよ。
- ONユニットの厳格なエラーハンドリングの魂を、Javaの例外機構に継承させろ。
この基本原則さえ守っていれば、移行後に「テスト環境では通ったのに、本番のビッグデータを入れた瞬間に金額が1円合わなくなった」なんていう悪夢のような夜間トラブルを防ぐことができる。
さあ、手を動かして、堅牢なコードを書き上げようぜ。何か詰まったらいつでも俺のところに聞きに来い。
