【入門編】定数定義における精度指定の罠 – PL/Iの基本構文とデータ制御実践ガイド

こんにちは!メインフレームの世界へようこそ。
JavaやCOBOLといった他のプログラミング言語の経験がおありなら、新しい言語を学ぶワクワク感と同時に、「レガシーの巨人」と呼ばれるPL/Iの独特な雰囲気に、少し身構えてしまっているかもしれませんね。

でも、安心してください。どんなに古く見える仕様や一見奇妙なルールであっても、そこには「なぜそう作られたのか」という明確な理由と美しい理屈があります。一つずつ紐解いていけば、決して怖いものではありませんよ。

今回は、PL/Iの基本データ型の中でも、特に初心者が「あれ?」とハマりやすい「定数定義における精度指定の罠」について、じっくりお話ししていきますね。

—

1. なぜ「数字」を書くだけでトラブルになるのか?(他言語との違い)

JavaやCOBOL、あるいはC言語などを触ってきた方なら、「数字をプログラムにそのまま書く(リテラル)」ことに対して、特に警戒心を抱くことはなかったはずです。「`100` と書けば数字の100だし、`0.05` と書けば小数の0.05でしょ?」って思いますよね。

もちろん、それは間違いではありません。しかし、PL/Iの世界では、あなたが何気なく書いたその「数字の見た目」が、コンパイラによって勝手に解釈され、時として思わぬ計算ズレや、最悪の場合はオーバーフローを引き起こすことがあるのです。

ここに、PL/I特有の「親切心」の裏返しが潜んでいます。

—

2. PL/Iのデフォルトルールを知ろう:FIXED BINARYとDECIMALの罠

PL/Iには、固定小数点数を表す主なデータ型として、以下の2つがあります。

  • FIXED DECIMAL (P’…’):十進数(人間が普段使う10進数)で正確に数値を保持します。金額計算などで大活躍します。
  • FIXED BINARY:二進数(コンピュータが内部で得意とする2進数)で数値を保持します。計算スピードが命のループ制御やフラグなどで好まれます。

さて、ここで問題です。プログラムの中に、次のような定数をポツンと書いたとします。

1
DCL WS_CNT FIXED BINARY(15) INIT(0);

「お、0で初期化しているんだな」と思いますよね。では、次の計算式はどうでしょう?

1
WS_CNT = WS_CNT + 1;

一見、何の問題もないように見えます。しかしコンパイラは、このソースコード上にある `1` という数字を見たとき、心の中でこうつぶやきます。

> 「ふむ、この `1` というリテラルは、小数点以下の指定がない整数だから……デフォルトで `FIXED DECIMAL(5, 0)` として扱おうかな!」

……お気づきでしょうか?
左辺の `WS_CNT` は `FIXED BINARY(15)` なのに、右辺で足されている `1` は勝手に `FIXED DECIMAL` に変換されてしまっているのです。

PL/Iは非常に「型に寛容(暗黙の型変換をたくさんやってくれる)」な言語です。そのため、異なるデータ型の足し算であっても、コンパイラが裏でこっそり型を合わせて計算してくれます。
しかし、この「こっそり変換」が、大規模なバッチ処理の現場ではパフォーマンスの低下や、極まれに予期せぬ丸め誤差の原因になるのです。

—

3. 実例で見る!定数定義の「落とし穴」と正しい処方箋

百聞は一見にしかず。実際のPL/Iコードを見てみましょう。
以下のコードは、マイグレーション現場でよく見かける、ちょっと危なっかしい定数計算の例です。

1
01 FIX-SAMPLE AREA,
05 WS_RATE FIXED BINARY(31) INIT(0);
05 WS_RESULT FIXED BINARY(31) INIT(0);

/ — 良くない例:リテラルの型が曖昧なケース — /
/ 3.14 と書くと、コンパイラは FIXED DECIMAL として扱います /
WS_RATE = 3;
WS_RESULT = WS_RATE 3.14;

このコード、動かしてもエラーにはなりません。PL/Iコンパイラが頑張って「ほうほう、FIXED BINARY と FIXED DECIMAL を掛け算するんだね。じゃあ型をそろえて計算してあげるよ」と裏でこっそり処理してくれるからです。

ですが、基幹システムのシビアな世界では、この「コンパイラの親切なお節介」が、CICSのオンライン画面や夜間バッチのCPU使用率をジワジワと押し上げる原因になります。また、桁数の大きな定数を扱うときには、精度の切捨てが発生してバグの温床になることも。

では、どう書けば安全なのか?

答えは簡単です。「定数(リテラル)を定義するときも、自分が意図したデータ型と精度をコンパイラにハッキリと伝える」ことです。

PL/Iでは、組み込み関数(BIF)やサフィックスを使って、定数の型を明示することができます。

1
01 FIX-SAFE AREA,
05 WS_RATE FIXED BINARY(31) INIT(0);
05 WS_RESULT FIXED BINARY(31) INIT(0);

/ — 良い例:型と精度を意識した記述 — /
/ FIXED BIN(31) の定数として明示的に扱う場合 /
WS_RATE = 3;

/ 小数点を扱う場合は FIXED DECIMAL なら明示的なピクチャー句、
あるいは FIXED BINARY に合わせた定数表現を心がける /

特に、定数を定義する `DECLARE`(DCL)文では、以下のように属性をピシッと揃えるのがプロの作法です。

1
/ 良いDECLAREの例:属性を省略せず、すべて明示する /
DCL TAX_RATE FIXED DECIMAL(5,2) VALUE(1.10); / 消費税率などはデシマルで正確に /
DCL MAX_LOOP FIXED BINARY(15) VALUE(100); / ループ回数はバイナリでスッキリと /

「たかが数字の `1` や `100` でしょ?」と思わずに、「私が書いたこのリテラル、コンパイル時に何型として解釈されているんだろう?」と一歩立ち止まって考えることが、信頼性の高いメインフレームシステムを作る第一歩になります。

—

4. まとめ:レガシーの海をスイスイ泳ぐために

いかがでしたでしょうか?
PL/Iの定数定義における精度の罠は、コンパイラの「良かれと思ってやってくれる暗黙の変換」から生じるものです。

  • 数字をそのまま書くと、予期せぬ `FIXED DECIMAL` に化けることがある。
  • `FIXED BINARY` との混在は、裏でこっそり型変換コストが発生している。
  • 大切な変数の初期値や定数は、属性を省略せず明示的に定義する癖をつけよう。

この3つを頭の片隅に置いておくだけで、保守やマイグレーションの際に「おや、このコード、なんだか怪しいぞ」と直感が働くようになります。

レガシーシステムの世界は奥が深いですが、仕組みさえ分かってしまえば、これほど堅牢でロジカルな世界はありません。怖がる必要は全くありませんよ。ぜひ、ご自身のプロジェクトでも意識してみてくださいね。それでは、次回の技術解説もお楽しみに!

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