浮動小数点数の「深い沼」へようこそ:PL/IにおけるFLOAT DECIMALとFLOAT BINARYの真実
こんにちは。長年メインフレームの深淵を見つめてきたシステムアーキテクトです。
皆さんは今、JavaやCOBOLの堅実な世界から、少しだけ「クセの強い」PL/Iという言語の入り口に立っていますね。
「予約語がない? 変数名に`IF`が使える?」なんて聞いて驚いたかもしれません。でも安心してください。PL/Iは非常に柔軟で、かつ一度理解すればこれほど表現力の高い言語はありません。今日は、初学者が最もハマりやすい「浮動小数点数の基数変換」という、ちょっと深くて面白い沼を一緒に探検してみましょう。
—
1. そもそもPL/Iには「予約語」がない?
まず、PL/Iの不思議なルールから紐解きましょう。C言語やJavaのように「`if`は予約語だから変数名には使えない」といった制約が、PL/Iには原則としてありません。
/i
/ 極端な例ですが、こんな宣言も文法的には通ってしまいます /
DCL IF FLOAT BINARY(21);
DCL THEN FLOAT DECIMAL(6);
IF = 3.14; / 変数IFに値を代入 /
コンパイラは、文脈(コンテキスト)を見て「これは変数だな」「これはキーワードだな」と判断します。この柔軟性は強力な武器ですが、読み手に混乱を招かないよう、現場では「予約語のような名前を変数に使わない」という暗黙のルールが大切にされています。皆さんも、まずは丁寧な命名を心がけてくださいね。
—
2. FLOAT DECIMALとFLOAT BINARYの「見えない壁」
さて、本題の浮動小数点数です。PL/Iには大きく分けて以下の二つが存在します。
- FLOAT DECIMAL: 10進数ベース(人間にとって直感的。科学計算の表記など)
- FLOAT BINARY: 2進数ベース(ハードウェアが最も得意とする形式)
一見、「どちらを使っても同じ計算結果になるでしょ?」と思いたくなりますよね。でも、ここが落とし穴なんです。
基数変換のオーバーヘッド
CPUは内部で2進数を使っています。そのため、`FLOAT BINARY`は計算の際、余計な変換を挟まずに高速に処理されます。一方で、`FLOAT DECIMAL`を使って計算を行うと、CPUは毎回「10進数から2進数への変換」を裏側で行わなければなりません。
これが積み重なると、大量のレコードを処理するバッチプログラムでは、無視できないパフォーマンス・ロスにつながります。「計算が遅いな?」と感じたら、まずはここを疑うのがアーキテクトの定石です。
丸め誤差の境界条件
さらに厄介なのが「丸め誤差」です。例えば、`0.1`という数字。これは10進数ではきれいな数字ですが、2進数で表すと無限小数になります。
/i
DCL A FLOAT DECIMAL(6); / 10進数ベース /
DCL B FLOAT BINARY(21); / 2進数ベース /
A = 0.1;
B = 0.1;
/ このとき、AとBの内部表現は完全には一致しません /
IF A = B THEN
PUT SKIP LIST(‘一致しました’);
ELSE
PUT SKIP LIST(‘誤差が出ています!’);
このコードを動かすと、ほとんどの環境で「誤差が出ています!」と表示されます。Javaの`float`や`double`で経験した「`0.1 + 0.2`が`0.3`にならない」という現象と同じことが、PL/Iでも起きるのです。
—
3. 実践:どう使い分けるのが正解?
では、現場でどう使い分けるべきか。私からのアドバイスはシンプルです。
1. 基本はFLOAT BINARYを使う:
計算速度とハードウェア親和性を優先します。科学技術計算や、厳密な10進数精度を必要としない集計処理では、こちらを選びましょう。
2. 固定小数点(FIXED DECIMAL)の検討:
もし皆さんが「金額」を扱っているなら、`FLOAT`を使ってはいけません。金融計算では、丸め誤差が許されないため、PL/Iの強力な機能である `FIXED DECIMAL`(固定小数点)を使用するのが鉄則です。
/i
/ 金額計算ならこれが正解 /
DCL SALES_AMOUNT FIXED DECIMAL(15, 2);
/ 15桁の精度、小数点以下2桁を厳密に保持 /
—
最後に:怖がる必要はありません
PL/Iは、古い言語ゆえに「仕様の自由度」が非常に高いです。それは同時に「書き手を選ぶ」ということでもあります。しかし、今回お話ししたように、データの型とCPUの動きを少し意識するだけで、バグの温床を未然に防ぐことができます。
「型が違うから計算が合わない」なんて悩んだときは、いつでもコンパイラが吐き出したリストや、メモリアドレスを覗いてみてください。PL/Iは、その挙動一つひとつに理由がある、実直な言語です。
次回の記事では、`FIXED DECIMAL`と`FLOAT`が混在した計算式で発生する、コンパイラの「自動変換」の深淵について解説しようと思います。またお会いしましょう!
