こんにちは!メインフレームの世界へようこそ。
JavaやCOBOLといった他のモダンな言語の経験をお持ちの方にとって、IBMメインフレームの世界、そしてそこで長年君臨してきた「PL/I(ピーエルアイ)」は、少し古めかしく、要塞のように堅固で近寄りがたい場所に感じられるかもしれませんよね。
特に、データ宣言で見かける `FIXED DECIMAL(5, 2)` なんて見慣れない記述や、「パック10進数」「COMP-3」といった聞いたこともない専門用語に出会うと、「うわっ、難しそう……変なデータを壊してしまわないだろうか」と、思わず冷や汗が出てしまうことでしょう。
でも、どうぞご安心ください。怖がる必要はまったくありませんよ。
今回は、PL/Iの基本構造に軽く触れつつ、基幹システムの心臓部である「FIXED DECIMAL(p,q) とパック10進数の世界」を、他の言語との違いを交えながら、そっと優しく紐解いていきたいと思います。
—
1. まずは基本のお作法:PL/Iプログラムの顔ぶれ
Javaなら `class` と `public static void main`、COBOLなら `IDENTIFICATION DIVISION` と `PROCEDURE DIVISION` がありますが、PL/Iの基本構造も非常にシンプルです。
PL/Iでは、プログラムを PACKAGE や PROCEDURE というブロックで組み立てます。そして、OS(JES/MTSなど)から最初に呼び出される「メインプログラム」には、お馴染みの魔法の呪文、`OPTIONS(MAIN)` を添えてあげます。
実際のコードの雰囲気を見てみましょう。
————————————————————–
- パック10進数の動作を確認するためのメインサンプル程序
————————————————————–
DEMO_PKG: PACKAGE;
DEMO_MAIN: PROCEDURE OPTIONS(MAIN);
- ここに変数宣言や処理を書いていきます
DECLARE WS_KING_SALARY FIXED DECIMAL(7, 2) INIT(12345.67);
PUT SKIP LIST (‘お給料の宣言値は: ‘, WS_KING_SALARY);
END DEMO_MAIN;
END DEMO_PKG;
どうですか? COBOLほど冗長ではなく、かといってC言語ほど記号だらけでもない、独特の気品を感じさせませんか? `PACKAGE` の中に `PROCEDURE` があり、最後にそれぞれ `END` で綺麗に蓋をする。この構造さえ押さえておけば、プログラムの全体像で迷うことはありません。
—
2. 本丸:「FIXED DECIMAL(p,q)」とパック10進数ってなに?
さて、今回の主役である `FIXED DECIMAL(p,q)` についてじっくり見ていきましょう。
Javaの `BigDecimal` や、COBOLの `PIC 9(5)V99 COMP-3` にピンと来る方なら、理解のスピードは一瞬です。でも、そうでない方のために、まずはイメージしやすい例えから入りますね。
メモリの節約術:1バイトに数字「2文字」を詰め込む技
通常のコンピュータの世界(例えばJavaの `int` や `long`)では、数字を2進数(バイナリ)で扱いますよね。しかし、銀行の通帳や給与明細など、絶対に1円の狂いも許されない「お金の計算」において、2進数特有の小数点の丸め誤差(10進数の `0.1` が2進数では無限小数になってしまう問題)は致命傷になります。
かといって、文字(キャラクター)として `12345.67` をそのまま文字列で持たせると、1文字につき1バイト使うので、無駄にメモリを食ってしまいます。
そこでメインフレームの出番です。
「1バイト(8ビット)の箱を半分こ(4ビットずつ)にして、そこに10進数の数字を1桁ずつギュッと詰め込んじゃおう!」
これが、パック10進数(IBM汎用機の用語では COMP-3 とも呼ばれます)の正体です。
4ビットあれば `0` から `9` まで(2の4乗=16通り)の数字を表現できるので、1バイトの上下(ゾーン部と数値部、あるいは前半と後半)にそれぞれ1桁の数字を押し込みます。そして、一番最後の右端(下位4ビット)には、その数字が「プラス(正)」なのか「マイナス(負)」なのかを表す符号(C, D, Fなど)を看板としてパタンと貼り付けます。
`FIXED DECIMAL(p, q)` の読み解き方
PL/Iでは、このパック10進数を `FIXED DECIMAL`(略して `DEC` と書くことも多いです)で宣言します。カッコの中の `(p, q)` は次のように指定します。
- `p` (Precision / 精度): 小数点を含めない、全体の有効桁数(最大15桁、拡張モードでは31桁までいけます)
- `q` (Scale / 尺度): そのうちの小数部が占める桁数
例えば、先ほどコードに出てきた `WS_KING_SALARY FIXED DECIMAL(7, 2)` ならば、
- 全体で 7桁 の数字が入る
- そのうち下 2桁 が小数部である
- つまり、整数部が 5桁($7 – 2 = 5$)、小数部が 2桁 となり、表現できる最大の値は `99999.99` から `-99999.99` までとなります。
—
3. 実務でハマりがちな「アライメント」と「変換コスト」の罠
さて、ここからがレガシーシステムのアーキテクトとしての腕の見所、そして初心者が一番ハマりやすいポイントです。
「パック10進数はメモリを節約できて素晴らしい!」……と、ここで油断してはいけません。CPUが計算をする際、実はこのパック10進数のまま直接ゴリゴリと演算しているわけではないのです。
計算の裏側で何が起きているか?
PL/Iプログラムの中で、次のような四則演算を行ったとします。
DECLARE A FIXED DECIMAL(5, 2) INIT(10.00);
DECLARE B FIXED DECIMAL(5, 2) INIT(3.00);
DECLARE C FIXED DECIMAL(7, 4);
C = A / B;
ここでコンパイラやCPUは、裏側で何をしているでしょうか?
パック10進数はあくまで「ストレージ(ファイルやDB、メモリ上の定位置)」でのコンパクトな姿です。CPUがレジスタに読み込んで割り算などの算術演算を行うためには、多くの場合、一時的に内部の専用形式(あるいはゾーン10進数など)に暗黙の型変換(データ・コンバージョン)を行っています。
さらに、`A` と `B` の小数点位置(qの値)が異なっていたり、代入先の `C` の精度が異なっていたりすると、コンパイラはアライメント(小数点位置合わせ)の調整コードをこっそり裏で生成します。
パフォーマンスへの影響
「たかが小数点合わせでしょ?」と思われるかもしれませんが、数百万件を処理する夜間バッチ処理において、この「暗黙のデータ変換とアライメント調整」がループの中で大量発生すると、CPUのサイクルを無駄に食いつぶし、バッチウィンドウ(処理完了期限)を大幅にオーバーする原因になります。
【シニアからのアドバイス】
- 計算に関わる変数の `FIXED DECIMAL(p, q)` の属性(特に小数点以下の `q` の値)は、できる限り揃えておきましょう。
- 無駄に大きな桁数(例えば、本当は2桁でいいのに `FIXED DECIMAL(15, 2)` など)を宣言すると、演算時のワークエリアやシフト処理のコストが増大します。必要最小限の精度を設計することが、メインフレームエンジニアの美学です。
—
4. おわりに:怖がらなくて大丈夫、ルールはシンプルです
いかがでしたでしょうか?
「FIXED DECIMAL(p,q)」と「パック10進数」、そして「アライメント」。言葉や仕様はレガシーで取っ付きにくそうに見えますが、その本質は「限られたメモリの中で、10進数の精度を100%保ったまま効率よく計算するための先人の知恵」に他なりません。
Javaの浮動小数点演算の誤差に悩まされた経験がある方なら、「おや、パック10進数ってなんて誠実で確実な奴なんだ!」と、むしろ愛着が湧いてくるはずです。
もし実際のマイグレーション現場やバッチ改修で、不可解なデータ定義やコンパイル警告に出会ったら、まずは「全体の桁数 `p`」と「小数点の位置 `q`」を紙に書いて整理してみてください。一つひとつ紐解いていけば、決して恐ろしいものではありませんよ。
それでは、快適なPL/Iプログラミングの旅を!次回の記事もお楽しみに。
