【実務・中級編】FLOAT BINARY(21)と(53)のIEEE 754準拠 – PL/Iの基本構文とデータ制御実践ガイド

おい、最近入った若手が「先輩、なんかこのバッチ、金額の計算結果が1円ズレるんです。COBOLからコンバージョンした時に浮動小数点の型でも狂ったんですかね?」って青い顔して駆け込んできたんだ。

お前ら、PL/Iの浮動小数点、特に `FLOAT BINARY(21)` と `FLOAT BINARY(53)` の挙動をナメてかかってないか?
IBMメインフレーム(z/Architecture)における浮動小数点演算は、単に「小数が扱える便利な型」じゃねえんだ。内部のIEEE 754バイナリ表現と、PL/Iコンパイラの暗黙の型昇格、そしてVSAMや固定長レコード入出力でのデータマッピングの仕様を完全に理解していないと、本番稼働後の決算処理で取り返しのつかない痛手を負うことになる。

今日は、現場の保守・開発で泥を被ってきた俺が、このIEEE 754準拠の浮動小数点の世界と、丸め誤差の悪夢について徹底的に叩き込んでやる。心して聞け。

—

1. 現場がハマる罠:`FLOAT BINARY(21)` と `FLOAT BINARY(53)` の正体

まず大前提として、PL/IにはCOBOLのような「予約語によるガチガチの制限」は少ないが、その分、宣言したデータ属性がコンパイル時にどう解釈されるかをプログラマが完全に把握していなければならない。

浮動小数点数(FLOAT)を定義するとき、お前らは何を基準に桁数を選んでいる?

  • `FLOAT BINARY(21)`(単精度 / Short浮動小数点)
  • 内部表現:総ビット数 32ビット(4バイト)
  • 仮数部:21ビット(符号部1ビット、指数部8ビットを除く)
  • 精度:10進数で約6桁から7桁の有効数字。
  • `FLOAT BINARY(53)`(倍精度 / Long浮動小数点)
  • 内部表現:総ビット数 64ビット(8バイト)
  • 仮数部:53ビット(符号部1ビット、指数部11ビットを除く)
  • 精度:10進数で約15桁から16桁の有効数字。

IBMの z/Architecture は、ハードウェアレベルで IEEE 754 規格に完全準拠した浮動小数点演算装置(FPU)を持っている。つまり、`FLOAT BINARY(21)` は IEEE 754 の 32ビット単精度、`FLOAT BINARY(53)` は 64ビット倍精度そのものとして高速に処理される。

ここで問題になるのが、「10進数の小数を2進数のバイナリで表現する」という宿命から生まれる丸め誤差だ。

—

2. なぜ丸め誤差が発生するのか?(メカニズムの核心)

人間が普段使う「0.1」という10進数は、2進数の世界(IEEE 754)では無限循環小数になる。
`0.1` を2進数で表すと `0.00011001100110011001100110011…(以下無限ループ)` だ。

有限のビット数(単精度なら21ビット、倍精度なら53ビット)に押し込める際、この無限小数はどこかで「ブチッ」と切り捨てられる。これが丸め誤差の正体だ。

特に `FLOAT BINARY(21)` は仮数部がたったの21ビットしかない。少し複雑な四則演算や累乗、割算を挟んだだけで、下位ビットの誤差が雪だるま式に膨れ上がり、実務で許されない「計算結果の不一致」を引き起こす。基幹システムの金額や金利計算で単精度を使おうもんなら、監査法人から即座にレッドカードを食らうぞ。

—

3. 実践:VSAM入出力とONユニットを考慮したPL/Iコード例

百聞は一見にしかずだ。実際のメインフレームのバッチジョブを想定したサンプルコードを見せてやる。
ここでは、外部ファイル(VSAMまたは順編成ファイル)から受領した10進の文字列データを安全に内部の `FLOAT BINARY(53)` に変換し、計算を行いつつ、演算エラー(オーバーフローやゼロ除算)が発生した場合には `ONユニット` でトラップする堅牢な構造にしている。

1
/ —————————————————————- /
/ PROGRAM-ID: FLTTST01 /
/ REMARKS : FLOAT BINARY (21) と (53) の精度比較とエラー制御 /
/ —————————————————————- /
FLTTST01: PROC OPTIONS(MAIN);

DCL W_INPUT_STR CHAR(15) INIT(‘123456.789012’); / 外部からの入力値 /

/ 浮動小数点変数の宣言 /
DCL F_SINGLE FLOAT BIN(21); / 単精度 (32bit) – 精度不足に注意 /
DCL F_DOUBLE FLOAT BIN(53); / 倍精度 (64bit) – 標準推奨 /

/ 比較用の固定小数点(DECIMAL FIXED) /
DCL D_DECIMAL DEC FIXED(15,4);

/ 演算結果出力用ピクチャ /
DCL OUT_P_SNGL PIC ‘——-9.999999’;
DCL OUT_P_DBL PIC ‘——-9.99999999999999’;

/ 組み込み関数(BUILTIN)の明示的宣言 /
DCL (FLOAT, FIXED, ABS) BUILTIN;

/ — エラー制御(ONユニット)の定義 — /
/ 浮動小数点のオーバーフローやゼロ除算をキャッチする /
ON OVERFLOW
BEGIN;
PUT SKIP LIST(‘【SEVERE ERROR】 浮動小数点演算でオーバーフローが発生しました。’);
SIGNAL ERROR;
END;

ON ZERODIVIDE
BEGIN;
PUT SKIP LIST(‘【SEVERE ERROR】 ゼロによる除算が検出されました。’);
SIGNAL ERROR;
END;

PUT SKIP LIST(‘=== FLOAT BINARY 精度比較処理 開始 ===’);

/ 1. 文字列から数値への変換と代入 /
/ 単精度への代入(ここで既に21ビットを超える情報が丸められる) /
F_SINGLE = W_INPUT_STR;

/ 倍精度への代入(53ビットの精度を保持) /
F_DOUBLE = W_INPUT_STR;

/ 2. 演算処理のシミュレーション(例:1.1を掛け合わせる) /
F_SINGLE = F_SINGLE 1.1E0;
F_DOUBLE = F_DOUBLE 1.1E0;

/ 3. 結果の出力と差異の確認 /
OUT_P_SNGL = F_SINGLE;
OUT_P_DBL = F_DOUBLE;

PUT SKIP EDIT(‘単精度 (FLOAT BIN(21)) 結果 : ‘, OUT_P_SNGL) (A, A);
PUT SKIP EDIT(‘倍精度 (FLOAT BIN(53)) 結果 : ‘, OUT_P_DBL) (A, A);

/ 4. 固定小数点(DECIMAL FIXED)との比較 /
/ 金額計算や厳密な数値管理には必ずDEC FIXED(p,q)を使うべきである /
D_DECIMAL = FIXED(W_INPUT_STR, 15, 4);
D_DECIMAL = D_DECIMAL 1.1;

PUT SKIP EDIT(‘固定小数点(DEC FIXED) 結果 : ‘, D_DECIMAL) (A, F(15,4));

PUT SKIP LIST(‘=== FLOAT BINARY 精度比較処理 終了 ===’);

RETURN;

END FLTTST01;

—

4. シニアアーキテクトからの現場の教訓・デバッグのコツ

このコードをコンパイルして実行してみれば一発で分かるが、`F_SINGLE` と `F_DOUBLE`、そして最後の `D_DECIMAL` の間には、下位桁で必ず微小なズレ(丸め誤差)が生じる。

レガシーシステムのマイグレーションや、COBOLからのコンバージョン案件で最も多い障害が、「旧システム(COBOLのCOMP-3 / パック十進数)では正確な値が出ていたのに、新システムのPL/I側で FLOAT を使ったために端数処理で合わなくなった」 というものだ。

現場でトラブルを防ぐための鉄則を3つ授けておく。

1. 金融・数量データには `FLOAT` を絶対に使わないこと
金額、数量、単価などの「正確性が求められるデータ」には、`FLOAT BINARY` や `FLOAT DECIMAL` ではなく、必ず `FIXED DECIMAL (p, q)`(パック十進数 / ゾーン十進数) を選択しろ。PL/IはDECIMALFIXED間の演算をハードウェアまたはライブラリで10進数のまま正確に処理するため、丸め誤差の心配が無用だ。
2. `FLOAT` を使うのは「科学技術計算」や「物理シミュレーション」の領域に限定する
どうしても `FLOAT` を使う必要ある(膨大なレンジを扱う物理量や統計処理など)場合は、迷わず `FLOAT BINARY(53)`(またはそれ以上の長精度 `FLOAT BINARY(109)`) を選べ。`FLOAT BINARY(21)` は、メインフレームのメモリやストレージが超高価だった昔の遺物だ。現代のハードウェアでケチる理由など1ミリもない。
3. コンパイルオプション(PRECISIONなど)の確認を怠るな
PL/Iコンパイラ(Enterprise PL/I)のオプション設定によっては、暗黙の型変換で勝手に単精度に落とされるケースがある。コーディング規約でデータ属性は常に明示的に記述する癖をつけろ。

浮動小数点の特性を甘く見ていると、深夜のバッチ障害で泣きを見るのはお前自身だ。
今日の解説を頭に叩き込んで、手戻りのない堅牢なPL/Iプログラムを組み上げろよ。期待してるぞ。

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