こんにちは!メインフレームの世界へようこそ。
JavaやCOBOLといった他のモダンな言語や基幹言語の経験がある方にとって、IBMメインフレームの「PL/I(ピーエルワン)」という名前は、少し古めかしく、どこか近寄りがたい要塞のように感じられるかもしれませんね。
特に、データ宣言のルールに出会ったとき、「えっ、桁数を省略したら勝手に決められちゃうの?」「なんだこの呪文みたいなオプションは……」と、冷や汗をかいた方も多いはずです。でも、安心してください。怖がる必要はまったくありません。今回は、PL/Iの基本データ型の中でも、実務で最も頻繁に登場する「固定小数点数(FIXED BINARY / DECIMAL)」の精度(Precision)のデフォルト規則とコンパイラオプションについて、一つずつ優しく紐解いていきましょう。
—
そもそも「精度(Precision)」ってなに?
Javaなら `int` や `long`、COBOLなら `PIC S9(4) COMP` のように、変数を宣言するときには「どれくらいの大きさの数字を入れられるか」をあらかじめ指定しますよね。
PL/Iでもそれは同じで、変数の型(属性)を宣言するときに、カッコ書きで桁数やビット数を指定します。これを精度(Precision)と呼びます。
例えば、こんな風に書きます。
1
DCL 売上金額 FIXED DECIMAL(9, 2); / 整数部7桁、小数部2桁の固定小数点数 /
「おっ、分かりやすいじゃん!」と思いましたよね。しかし、PL/Iの奥が深いところ(そして、たまに初心者を悩ませる所)は、「この精度をあえて省略できてしまう」という仕様にあるのです。
—
精度を省略するとどうなる?(デフォルトの恐怖と優しさ)
もし、うっかり以下のように精度を書かずに変数を宣言してしまったら、コンパイラはどう解釈するでしょうか?
1
DCL カウンタ FIXED BINARY; / 精度が省略されている! /
DCL 金額 FIXED DECIMAL; / こっちも省略されている! /
「おいおい、勝手に省略してエラーにならないの?」と心配になりますよね。
エラーにはなりません。PL/Iのコンパイラは、親切心(あるいはレガシーな歴史的背景)から、「あなたが書かなかったんだから、俺の方で標準(デフォルト)のサイズを勝手におぎなっておくね!」と、デフォルトの精度を自動的に補完してくれます。
問題は、その「デフォルト値」が一体いくつなのか、ということです。
1. FIXED BINARY(二進固定小数点数)の場合
バイナリ(2進数)の世界では、マシンのアーキテクチャ(ワード境界など)との相性が重視されます。
IBMメインフレーム(z/Architecture)の世界では、デフォルトの精度はコンパイラのデフォルト設定に依存しますが、一般的には `FIXED BINARY(15)`(つまり半精度整数、Hのサイズ)または `FIXED BINARY(31)`(全精度整数、Fのサイズ)として扱われます。
特に何も指定しない場合、コンパイラや環境によっては15ビットや31ビットになり、思わぬオーバーフローを引き起こす原因になることがあります。
2. DECIMAL(十進固定小数点数)の場合
こちらは、私たちが普段使う10進数の世界です。
精度を省略して `FIXED DECIMAL` とだけ書いた場合、なんとデフォルトで `(5, 0)`(5桁、小数部なし)として扱われるのが一般的です。
「たった5桁!?」と思いましたよね。そうなんです。大きな金額を扱うバッチプログラムなどでこれをやってしまうと、すぐに桁あふれ(S0C7などのデータ例外)を起こして夜間バッチが盛大にクラッシュする……なんていうドラマ(悪夢)が、レガシー世界の現場では時々起きていたりします。
—
コンパイラオプション「LIMITS」による挙動の変化
「じゃあ、デフォルトがいくつになるかは、運任せなの?」
いいえ、そこをビシッと統制するのが、コンパイル時に指定するLIMITSオプションです。
IBMのPL/Iコンパイラには、ソースコードの記述をコンパイルする際に、さまざまな制約やデフォルト値を変更・監視するためのオプションが用意されています。その代表格が `LIMITS` サブオプションです。
例えば、コンパイル時に以下のようなオプションを指定したとします。
LIMITS(FIXEDDEC(15), FIXEDBIN(31))
これは、コンパイラに対してこう命令しています。
- 「もしソースコード内で `FIXED DECIMAL` の精度が省略されていたら、勝手に `(15, 0)` だと思ってコンパイルしなさい!」
- 「もし `FIXED BINARY` の精度が省略されていたら、勝手に `(31)` だと思ってコンパイルしなさい!」
アーキテクチャ移行時の「落とし穴」
ここで、JavaやCOBOLから来たエンジニアが一番ハマりやすいポイントをお伝えしておきます。
それは、「開発環境(手元のPCやテスト環境)と、本番のメインフレーム環境で、コンパイラオプションのデフォルト値が微妙に違っていることがある」という点です。
例えば、マイグレーション(移行)プロジェクトで、古いメインフレームのソースコードをごっそり新しいコンパイラ環境に移し替えたとします。その際、JCL(ジョブ制御言語)やコンパイル手順書にあるLIMITSオプションの見落としにより、これまで「暗黙のデフォルト」に頼っていた計算処理の内部精度が変わり、計算結果の端数処理や丸め誤差が微妙にズレてしまう……というトラブルが起きた実例があります。
だからこそ、プロのメインフレームアーキテクトは口を揃えてこう言います。
> 「ソースコードを書くときは、面倒くさがらずに精度を必ず明示しなさい。デフォルトに頼るな!」 と。
—
実務で使える!安全なデータ宣言の書き方
それでは、ここまでの知見を踏まえて、実務のバッチプログラムでそのまま使える、美しく安全なPL/Iのデータ宣言のサンプルコードを見てみましょう。
1
/ ————————————————– /
/ 顧客マスタ更新プログラム:安全なデータ定義のサンプル /
/ ————————————————– /
MY_PROGRAM: PROC OPTIONS(MAIN);
/ 【良い例】桁数をしっかりと明示しています /
DCL W_CUSTOMER_ID FIXED DECIMAL(7, 0) INIT(0); / 顧客ID:7桁(整数) /
DCL W_UNIT_PRICE FIXED DECIMAL(9, 2) INIT(0); / 単価:整数7桁、小数2桁 /
DCL W_QUANTITY FIXED BINARY(15) INIT(0); / 数量:半精度バイナリ /
DCL W_TOTAL_AMOUNT FIXED DECIMAL(11, 2) INIT(0); / 合計金額:余裕を持った11桁 /
/ 【避けるべき例】精度が省略されており、コンパイラ依存になる /
/ DCL DANGEROUS_AMT FIXED DECIMAL; / / ← デフォルト(5,0)になりがちで危険! /
/ 処理ロジックのイメージ /
W_UNIT_PRICE = 1250.50;
W_QUANTITY = 100;
/ 計算時の自動スケーリングが行われる /
W_TOTAL_AMOUNT = W_UNIT_PRICE W_QUANTITY;
PUT SKIP LIST(‘計算完了! 合計金額 = ‘, W_TOTAL_AMOUNT);
END MY_PROGRAM;
コードのポイント解説
1. `FIXED DECIMAL(9, 2)` のように、整数部と小数部を明確にする
金額や単価を扱うときは、必ず `DECIMAL` を使い、小数点以下の桁数まで意識して宣言します。これにより、予期せぬ丸め誤差や桁あふれを未然に防げます。
2. カウンターやループ制御には `FIXED BINARY` を選ぶ
ループの回数や配列のインデックスなど、純粋な数値計算や高速な処理が求められる場所には、マシンのレジスタと相性が良い `FIXED BINARY`(通常は `(15)` や `(31)`)を指定するのが定石です。
—
多様な言語を経験されてきた皆さんなら、PL/Iのデータ型も「要するに型のサイズと表現方法の話ね」と、本質はすぐに掴めるはずです。ただ、歴史が長い分、「省略したときの暗黙のルール」がコンパイラオプションと深く結びついている点が少しユニークなところ。
「精度は省略せず、自分の手できちんと書き下ろす」。これさえ守っていれば、LIMITSオプションの差異に怯える必要もありません。
レガシーシステムの海原へ漕ぎ出すあなたの航海が、安全で実りあるものになりますように。もしまた分からない仕様に出会ったら、いつでも気軽に扉を叩いてくださいね!
