浮動小数点数の「罠」を越えて——PL/Iで扱う数値と精度の深淵
こんにちは。メインフレームの世界へようこそ。
普段、Javaで `double` を使い、COBOLで `COMP-3` を扱っている方々からすると、PL/Iの数値型は少しばかり「クセが強い」と感じるかもしれません。
今回は、基幹システムのマイグレーション現場でエンジニアを最も悩ませるトピックの一つ、「浮動小数点数(FLOAT)」の精度と丸め誤差について紐解いていきます。「計算結果がわずかにズレる……」そんな恐怖に立ち向かうための心得を、一緒に学んでいきましょう。
—
1. PL/Iの浮動小数点:BINARYかDECIMALか
PL/Iには大きく分けて二つの浮動小数点型があります。
- FLOAT BINARY: 2進浮動小数点(IEEE 754準拠)。計算速度が爆速。
- FLOAT DECIMAL: 10進浮動小数点。人間が直感的に計算できる(10進数に近い)。
Javaの `float` や `double` は「2進」ですが、PL/Iでは「どちらを使うか」を意図的に選ぶ必要があります。ここで重要なのは、「コンピュータは2進数で考えている」という事実です。
なぜ誤差が起きるのか?
例えば `0.1` という数字。これを2進数で表現しようとすると、`0.0001100110011…` と無限ループになります。コンピュータのメモリ(ビット)は有限ですから、どこかで切り捨て(丸め)が発生します。これが、バッチ処理の合計金額が1円合わない……なんていう悲劇の元凶なのです。
—
2. 実際にコードで見てみよう
PL/Iの基本的な構造(`PROCEDURE OPTIONS(MAIN)`)の中で、この誤差がどう現れるか確認してみましょう。
/i
TEST_FLOAT: PROCEDURE OPTIONS(MAIN);
/ FLOAT BINARY(21)は、おおよそ7桁の精度を持つ単精度浮動小数点数です /
DCL VALUE_A FLOAT BINARY(21) INIT(0.1);
DCL VALUE_B FLOAT BINARY(21) INIT(0.3);
DCL RESULT FLOAT BINARY(21);
/ 0.1 + 0.2 は果たして 0.3 になるでしょうか? /
RESULT = 0.1 + 0.2;
IF RESULT = 0.3 THEN
PUT SKIP LIST(‘成功!一致しました’);
ELSE
PUT SKIP LIST(‘失敗!誤差が発生しました:’, RESULT);
/ ここでは 0.30000001 などの値が表示されることが多いです /
END TEST_FLOAT;
見ての通り、コンピュータは「0.1と0.2を足したから0.3だよね」とは考えません。「ビットの並び」で計算するため、わずかなゴミが残るのです。
—
3. 実務で「誤差」を避けるための鉄則
メインフレームの基幹業務、特に金融・会計システムで「浮動小数点」をそのまま使うのは実は御法度です。以下のポイントを覚えておいてください。
① 「比較」にはイプシロン(許容誤差)を使う
浮動小数点数同士を `IF A = B` で比較するのは厳禁です。「差分が非常に小さい値(例えば 0.000001)」以下なら「等しい」とみなす、という書き方がプロの流儀です。
/i
/ 差の絶対値が十分に小さければ等しいとみなす /
IF ABS(RESULT – 0.3) < 1E-6 THEN
PUT SKIP LIST('実質的に一致しました');
② 金額計算には「FIXED DECIMAL」を使う
これが一番の解決策です。PL/Iには `FIXED DECIMAL(15, 2)` のような、「固定小数点数」という型があります。これはCOBOLの `PIC S9(13)V99` と同じ考え方で、誤差が発生しません。金額を扱う時は、迷わずこれを選んでください。
—
まとめ:怖がる必要はありません
PL/Iの浮動小数点数は、一見すると不親切で「なぜこんなズレるんだ!」と怒りたくなるかもしれません。しかし、これはコンピュータが電気信号を高速に処理するための「最適化の結果」に過ぎないのです。
- 科学計算や統計 → 速度重視で `FLOAT BINARY`
- お金や在庫 → 精度重視で `FIXED DECIMAL`
この二つを使い分けるだけで、あなたの書くPL/Iプログラムは圧倒的に堅牢なものになります。
もし、レガシーコードを読んでいて「なぜここで `FLOAT` を使っているんだ?」と疑問に思ったら、それは当時のシステムアーキテクトが「速度」を優先した証拠です。その意図を汲みつつ、現在の要件に合わせて型を再考する。それこそが、メインフレーム・アーキテクトへの第一歩ですよ。
また次回、さらにディープなPL/Iの世界でお会いしましょう!
