こんにちは!メインフレームの世界へようこそ。
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つを頭の片隅に置いておくだけで、保守やマイグレーションの際に「おや、このコード、なんだか怪しいぞ」と直感が働くようになります。
レガシーシステムの世界は奥が深いですが、仕組みさえ分かってしまえば、これほど堅牢でロジカルな世界はありません。怖がる必要は全くありませんよ。ぜひ、ご自身のプロジェクトでも意識してみてくださいね。それでは、次回の技術解説もお楽しみに!
