【テクニカル・上級編】FLOAT BINARYとFLOAT DECIMALの精度と丸め誤差 – PL/Iの基本構文とデータ制御実践ガイド

浮動小数点の深淵:PL/IにおけるFLOAT BINARYとFLOAT DECIMALの「誤差」と、その設計的帰結

メインフレームの現場で、私たちは「数字の正しさ」を疑うことを許されません。しかし、PL/Iにおいて`FLOAT BINARY`と`FLOAT DECIMAL`を扱うとき、その「正しさ」は極めて危ういバランスの上に成り立っています。

特に、JavaやC#といったマネージド言語への移行を控えたシステムアーキテクトにとって、この「浮動小数点の挙動」は、バッチの整合性やオンラインのトランザクション結果を根本から覆す地雷原となり得ます。今日は、コンパイラの裏側にある物理的な挙動と、現場で遭遇するトラブルの深層に切り込みます。

1. FLOAT BINARY vs FLOAT DECIMAL:内部表現の決定的な差異

PL/Iにおいて`FLOAT BINARY(p)`と`FLOAT DECIMAL(p)`は、単なる精度の違いではありません。内部的な基数(Radix)の違いが、丸め誤差の蓄積パターンを決定づけます。

  • FLOAT BINARY: 2進浮動小数点。ハードウェア(IBM z/Architecture)の浮動小数点レジスタと直結しており、計算速度は極めて高速です。しかし、0.1のような「10進数で単純な小数が、2進数では無限小数になる」問題から、ビジネスロジックでの「1円のズレ」を誘発します。
  • FLOAT DECIMAL: 10進浮動小数点(DFP: Decimal Floating Point)。こちらはIBMのメインフレームが誇るハードウェアアクセラレーションを活用し、10進数ベースで演算を行うため、財務計算の直感に適合します。

【教訓】:銀行勘定系や税計算において、`FLOAT BINARY`の使用は原則禁止です。マイグレーション時には、レガシー側の`FLOAT`型が何を意図して定義されたかを見極め、`FIXED DECIMAL`(パックデシマル)へ置き換えるのが鉄則です。

2. 現場のコードで見る「精度」の境界線

以下のコードは、精度指定が計算結果にどう影響するかを示した典型的な例です。

/i
/ FLOAT BINARYとDECIMALの精度比較 /
/ 精度を明示的に指定しない場合、デフォルト値に依存する危険がある /
DCL A FLOAT BINARY(21); / 短精度相当 /
DCL B FLOAT BINARY(53); / 倍精度相当 /

A = 1.1E0;
B = 1.1E0;

/ ここでAとBを比較すると、微妙な誤差で一致しない可能性がある /
IF A ^= B THEN
PUT SKIP LIST(‘浮動小数点誤差による不一致が発生’);

移行先であるJavaの`double`(IEEE 754 2進浮動小数点)へ単純にマッピングすると、`FLOAT DECIMAL`が持っていた「10進数としての正確性」が失われ、テストフェーズで「なぜか小銭が合わない」という怪現象に悩まされることになります。`BigDecimal`クラスへの強制変換が、どれほどコストのかかる改修か、身に染みている方も多いはずです。

3. アベンド(ABEND)解析とデータ制御の秘訣

PL/Iでのデータ操作における最大のリスクは、浮動小数点のオーバーフローだけではありません。むしろ、パックデシマル(FIXED DECIMAL)の符号反転バグが、運用現場の深夜を襲う定番です。

パックデシマルの符号問題

DB2やCICSの通信データにおいて、外部から不正な符号ビット(例えば`X’0C’`であるべきところが`X’0F’`など)が混入した際、PL/Iの算術演算は容赦なく`S0C7`(Data Exception)を投げて停止します。

/i
/ DB2からのフェッチ時、数値変換エラーを防ぐ防御的コーディング /
DCL WORK_AMT FIXED DEC(15,2);
/ VALIDATE関数やON-UNITを活用し、異常値を補足する /
ON CONVERSION BEGIN;
/ ここでログを出力し、不正なデータを持つレコードを特定する /
PUT SKIP LIST(‘データ変換エラーが発生。入力値をチェックしてください’);
GO TO ERROR_HANDLING;
END;

4. マイグレーションに向けたアーキテクチャの視点

移行プロジェクトのテックリードとして、以下の3点は必ず確認してください。

1. コンパイラオプションの差異: `FLOAT(BE390)`(IBM形式)と`FLOAT(IEEE)`(IEEE 754)の混在は、計算順序一つで結果を狂わせます。移行先がIEEE準拠であれば、レガシー側でも一度コンパイラオプションをIEEEに寄せて、誤差の出方をシミュレーションすべきです。
2. ポインタによる動的メモリ操作: `ADDR()`関数と`BASED`変数を使った動的メモリ操作は、C言語的なアプローチですが、現代のJavaのガベージコレクション環境とは相性が最悪です。この領域は、ビジネスロジックから完全に切り離し、専用のシリアライザ層としてカプセル化する必要があります。
3. CICS/DB2エッジケース: CICSのCOMMAREAを介したデータ受け渡しで、浮動小数点をバイナリコピーしている場合、移行先とのエンディアンや表現形式の差異で即死します。構造体定義を再定義し、JSONやXML等の中間形式へ正規化する工数を、プロジェクト計画の初期に盛り込んでください。

結びに代えて

PL/Iは、ハードウェアの能力を極限まで引き出すための「魔法の杖」でした。しかし、その魔法は時に、浮動小数点のわずかな丸め誤差という形で、開発者の足元をすくい上げます。

マイグレーションは、単なるコードの書き換えではありません。メインフレームが数十年にわたって蓄積してきた「数値に対する厳格な設計思想」を、現代の言語体系へ翻訳する作業です。理論的な正しさと、現場で泥を被ってきた経験の両輪を持って、この難局を乗り越えていきましょう。

次回の記事では、「CICSオンライン処理におけるトランザクション再起動の設計」について、さらにディープに掘り下げます。期待していてください。

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