こんにちは。基幹システムの現場で日々、メインフレームのうなり声を聞きながらPL/Iコードと格闘しているシニアアーキテクトだ。
君も経験があるだろう? 夜間バッチの真っ最中、突如として発生する「S0C7(データ例外)」や、前任者が残した謎の数値ズレ。原因を突き詰めていくと、大抵たどり着くのが `FIXED BINARY`(2進固定小数点数) と `FIXED DECIMAL`(10進固定小数点数) の間の、うっかり見落としがちな型変換ルールだ。
現代のオープン系言語から来たプログラマからすると、「数字は数字なんだから勝手に合せてくれよ」と思うかもしれない。だが、IBM汎用機のPL/Iの世界では、ビットとバイト、そしてゾーン・パック10進数の世界が厳密なルールで支配されている。ここを曖昧にしていると、コンパイラは文句も言わずに通すくせに、本番データで痛い目を見る。
今日は、この「固定小数点数の相互変換」の裏側と、実務で絶対に知っておくべき境界条件について、私の現場の経験を交えて叩き込んでやろう。
—
1. 2進と10進:基本思想のちがいと変換の優先順位
まずは基本のおさらいだ。この2つのデータ型は、メモリ上の持ち方も、演算のコストも全く異なる。
- `FIXED DECIMAL (p, q)` (COMP-3など)
- 10進数の1桁を4ビット(ニブル)で表現し、最後に符号(C, D, Fなど)を持つ。
- 人間にとって直感的であり、金額や数量など誤差が許されないビジネス計算の王道だ。
- `FIXED BINARY (p, q)` (COMP / COMP-4)
- ハードウェア(CPU)が直接計算できる2進数の世界。
- ループ制御のカウンタや、内部的なフラグ、高速な算術演算に向いている。
算術演算や代入における「暗黙の型変換」
これら異なる型の間で演算や代入を行うとき、PL/Iコンパイラは自動的に型変換(コンバージョン)を行う。ここで重要なのが 「どちらの精度が優先されるか」 というルールだ。
基本原則として、`FIXED BINARY` と `FIXED DECIMAL` が混在する式では、原則として `FIXED DECIMAL` 側が `FIXED BINARY` に昇格(Promotion)されるか、あるいはコンパイラが中間ワークエリアでよしなに計算する。だが、この「よしなに」がくせ者だ。
特に、VSAMファイルのレコード定義(`DECLARE`文によるレイアウト)と、ワーキングストレージ上の演算用変数の間で暗黙の変換が発生するとき、予期せぬ精度落ちやパフォーマンスの劣化を招く。
—
2. 精度損失とオーバーフローの境界条件
現場で最も恐ろしいのは、コンパイルエラーにならず、静かにデータが切り捨てられる(あるいはオーバーフローしてS0C7に向かう)瞬間だ。
境界条件の罠
1. 整数部・小数部の桁あふれ
- 例えば、`FIXED DECIMAL(5, 2)`(全体5桁、小数2桁。最大 999.99)の値を、十分な整数部を持たない `FIXED BINARY` に無理やり代入しようとすると、上位桁が容赦なく切り捨てられる。
2. バイナリの「p」の本当の意味
- `FIXED BINARY(15)` は、符号を含めて15ビットを意味する(約32文書)。一方、`FIXED DECIMAL(5)` は5桁(最大 99,999)だ。
- この桁数(p)の定義の仕方の違いにより、相互代入時にスケールファクター(小数点位置 `q`)の調整ズレが発生する。
—
3. 実践コード:VSAMアクセスと安全な型変換
百聞は一見に如かず。実際のバッチプログラムを想定したコードを見てみよう。
ここでは、VSAMから読み込んだパック10進数(`FIXED DECIMAL`)の金額データを、高速なカウンタや計算用変数(`FIXED BINARY`)に安全に受け渡し、さらにビルトイン関数を使って精度を制御する例を示す。
1
/ —————————————————- /
/ FIXED BINARY と FIXED DECIMAL の相互変換サンプル /
/ —————————————————- /
CONVTEST: PROC OPTIONS(MAIN);
DCL 1 VSAM_IN_RECORD,
5 WK_CUST_ID CHAR(5),
5 WK_RAW_AMNT FIXED DECIMAL(9,2) COMP-3; / 10進(金額) /
DCL HOSHU_RATE FIXED BINARY(15,4) VALUE(1.0825); / 2進(率) /
DCL WK_CALC_RESULT FIXED DECIMAL(11,4) COMP-3; / 計算結果用 /
DCL OUT_AMNT_MSG CHAR(20);
/ — 1. FIXED DECIMAL から FIXED BINARY への演算 — /
/ VSAMからの入力を想定したダミー値設定 /
WK_RAW_AMNT = 123456.78;
DISPLAY(‘— 変換前データ —‘);
DISPLAY(‘WK_RAW_AMNT (DECIMAL): ‘ || CHAR(WK_RAW_AMNT));
/
- 【注意】FIXED DECIMAL と FIXED BINARY の乗算
- PL/Iは自動的にDECIMALをBINに寄せるか中間形式で計算するが、
- 意図しない精度落ちを防ぐため、ビルトイン関数(FIXED等)で
- 明示的に型と精度をキャスト(制御)するのがプロの技だ。
/
/ 暗黙の変換に頼らず、一度DECIMALとして安全に計算する場合 /
WK_CALC_RESULT = WK_RAW_AMNT HOSHU_RATE; / ここで暗黙の変換が発生 /
DISPLAY(‘CALC_RESULT (DECIMAL): ‘ || CHAR(WK_CALC_RESULT));
/ — 2. 厳密な精度制御とBUILTIN関数の活用 — /
/
- FIXEDビルトイン関数を使い、変換時の精度(p,q)をプログラマが明示する。
- これにより、コンパイラの気まぐれな中間精度拡張による予期せぬ挙動を防ぐ。
/
BEGIN;
DCL SAFE_BIN_VAL FIXED BINARY(31,0);
/ 小数点を切り捨てて純粋な整数(BIN)に落とし込む /
SAFE_BIN_VAL = FIXED(WK_RAW_AMNT, 31, 0);
DISPLAY(‘SAFE_BIN_VAL (31,0): ‘ || SAFE_BIN_VAL);
END;
END CONVTEST;
—
4. ONユニットによる例外制御とデバッグの極意
もし君が保守しているレガシーコードで、どうしても予期せぬデータ混入(スペースや不正なゾーン文字など)が避けられない環境にいるなら、ONユニットの出番だ。
`CONVERSION` 条件をトラップすることで、S0C7でバッチが即死するのを防ぎ、エラーログを残して処理をバイパスする設計が求められる。
1
/ 不正文字や桁あふれによるコンバージョンエラーの捕捉 /
ON CONVERSION BEGIN;
DISPLAY(‘【警告】数値変換エラーを検出しこしました。デフォルト値で継続します。’);
/ 必要に応じてエラーフラグを立てる、または代替値を設定 /
END;
もっとも、ONユニットはあくまで「最後の安全網」であって、根本的な解決策ではない。DB2やVSAMの定義と、PL/I側の `DECLARE`(特に `PIC` 句や `FIXED BINARY / DECIMAL` の選択)のズレを正しく一致させることが、システムアーキテクトとしての本来の仕事だ。
—
シニアからのメッセージ
PL/Iは非常に表現力が豊かで、コンパイラが賢いがゆえに、プログラマの「甘え」を許容してしまう言語でもある。「動いているから大丈夫」ではなく、「なぜこの精度(p, q)で定義されているのか」をレイアウト図やCOPY句を遡って突き詰めてほしい。
マイグレーションや大規模改修の際、このデータ型の違いを甘く見たせいで、移行直後に金額の端数が数円ズレるという悪夢のようなトラブルを私は何度も見てきた。
基本データ型の特性を掌の上で転がすように理解すること。それが、君を真のメインフレーム・エンジニアへと引き上げる第一歩だ。さて、次のタスクに取り掛かろうか。
