【入門編】FIXED DECIMALのパック10進数形式(COMP-3) – PL/Iの基本構文とデータ制御実践ガイド

こんにちは!メインフレームの世界へようこそ。
JavaやCOBOLといったモダン、あるいはビジネス寄りの言語を経験されてきた方にとって、IBMメインフレームの「PL/I」という名前だけでも、何やら古めかしくて難解な要塞のように感じてしまうかもしれませんね。

「なんだかよく分からない記号が多い」「メモリの1バイト単位まで意識させられそう……」と、不安になっていませんか?
でも、どうぞご安心ください。一つひとつの仕様を紐解いていけば、PL/Iは非常に論理的で、私たちプログラマの意図に忠実に動いてくれる頼もしい相棒です。

今回は、基幹システムの勘定系データなどで避けて通れない、`FIXED DECIMAL`(パック10進数 / COMP-3)の核心に迫ります。メモリの中でデータがどう息づいているのか、その秘密を一緒に覗いてみましょう!

—

1. なぜ「パック10進数(COMP-3)」が必要なの?

私たちが普段使うコンピュータは、数値を「2進数(バイナリ)」で理解しますよね。Javaの `int` や `long`、COBOLの `COMP` も基本はこれです。

しかし、金融システムの世界では、1円の狂いも許されません。
例えば、浮動小数点数を使うと、`0.1 + 0.2` が `0.30000000000000004` のようになってしまう「丸め誤差」問題が起きますよね。銀行の通帳でこんな誤差があったら大騒ぎです。

そこで登場するのが、人間にとって馴染み深い「10進数」を、そのままの精度で正確にメモリに閉じ込める方式です。
COBOLerの皆さんなら「COMP-3」と言えばピンとくるはず。PL/Iでは、これを`FIXED DECIMAL`という属性で表現します。

—

2. `FIXED DECIMAL(p, q)` の呪文を解読する

PL/Iで数値を宣言するとき、こんな見慣れない記述に出会います。

1
DCL SALARY FIXED DECIMAL(7, 2);

この `(7, 2)` って何だ?と思いますよね。怖がらずに分解してみましょう。

  • `p`(全体桁数 / Precision): 符号を除いた全体の最大桁数です。上記の例なら「7桁」。
  • `q`(小数点以下の桁数 / Scale): そのうち小数点が何桁分あるか。上記の例なら「2桁」。

つまり、`FIXED DECIMAL(7, 2)` と書いたら、「整数部分が5桁、小数部分が2桁で、合計7桁の数字を正確に保管できる領域をくれ!」とコンパイラにお願いしていることになるのです。

—

3. メモリの中はどうなっている?(パック形式の仕組み)

さて、ここからが本題です。「パック10進数」とは一体どういう状態を指すのでしょうか?

通常の文字(ゾーン10進数)として数字をメモリに置くと、1桁につき1バイト(8ビット)使います。「123」なら3バイトですね。
しかし、パック10進数(COMP-3)は、その名の通り「ギュッとパッキング(圧縮)」します。

ルールは非常にシンプルです。
「1バイトの中に、数字を2文字分(2ニブル=4ビット×2)詰め込み、最後の空いた右端の半バイト(4ビット)に符号(プラスやマイナス)を入れる」これだけです。

データのレイアウトをイメージしてみよう

例えば、先ほどの `FIXED DECIMAL(5, 0)` で `+12345` という数字を格納したとします。
全体で5桁ですね。これをパックすると、何バイトになるでしょうか?

計算式はこうです:
$$\text{バイト数} = \lfloor \text{全体桁数} / 2 \rfloor + 1$$

5桁の場合、$(5 / 2) + 1 = 3.5$ の切り捨てで 3バイト になります。

メモリの3バイト(24ビット)を覗いてみると、こんな風に並んでいます。

  • 1バイト目: `[ 0001 ] [ 0010 ]` (「1」と「2」)
  • 2バイト目: `[ 0011 ] [ 0100 ]` (「3」と「4」)
  • 3バイト目: `[ 0101 ] [ 1100 ]` (「5」と、符号を表す「C」)

一番最後の `1100`(16進数で `C`)は何でしょう?これが「プラス(正)」を表すゾーンです。
(ちなみにマイナスの場合は、ここが `D` になったりします。IBMメインフレームの世界ではお馴染みの光景ですね!)

小数点(,q)が指定されていても、このパック構造の本質は変わりません。小数点自体はメモリ上に存在せず、コンパイラが「ここにお手製の小数点があるものとして計算するぞ」と位置を記憶しているだけなのです。

—

4. 実践!PL/Iコードで挙動を確認する

百聞は一見に如かず。実際にPL/Iでどのように定義し、データを扱うのか、実務でよくあるスタイルのコードを見てみましょう。

1
/ ———————————————— /
/ FIXED DECIMAL の動作サンプルプログラム /
/ ———————————————— /
TEST_DEC: PROC OPTIONS(MAIN);

/ 変数の宣言 /
/ 総合計7桁、うち小数2桁の単価 /
DCL WK_UNIT_PRICE FIXED DECIMAL(7, 2) INIT(12345.67);

/ 総合計9桁、小数なしの数量 /
DCL WK_QTY FIXED DECIMAL(9, 0) INIT(100);

/ 計算結果を受け取る金額エリア(桁あふれを防ぐため広めに取るのが鉄則!) /
DCL WK_TOTAL FIXED DECIMAL(11, 2);

/ 実際の計算処理 /
WK_TOTAL = WK_UNIT_PRICE WK_QTY;

/ 結果の出力(PUT LIST) /
PUT SKIP EDIT (‘単価 :’, WK_UNIT_PRICE) (A, F(10,2));
PUT SKIP EDIT (‘数量 :’, WK_QTY) (A, F(10,0));
PUT SKIP EDIT (‘合計 :’, WK_TOTAL) (A, F(13,2));

END TEST_DEC;

ここがポイント!実務の現場からのアドバイス

COBOLやPL/Iで最も恐ろしいバグの一つが「S0C7(データ例外 / 10進数データ例外)」です。
もし、外部から読み込んだファイルやデータベースの領域に、パック10進数として正しくないゴミデータ(文字や不正なゾーン)が入っていた状態で上記のような計算をしようとすると、プログラムは即座に異常終了(アボート)します。

「FIXED DECIMALを使うときは、常にその中身が正しいパック形式であること(あるいは初期化されていること)」を意識するのが、優秀なメインフレーム・エンジニアへの第一歩です。

—

おわりに

いかがでしたでしょうか?
「パック10進数(COMP-3)」という言葉を聞くと、何やら難解な呪文のように思えますが、要するに「1バイトのなかに数字を2つずつ詰め込んで、最後に符号をチョコンと乗せた、誤差のないエコな数値形式」です。

PL/Iは、こうしたハードウェアの特性(バイト境界やニブル単位の処理)をプログラマが意識通りにコントロールできる、非常にパワフルな言語です。最初はギョッとするかもしれませんが、仕組みが分かってくると、これほど信頼性の高いデータ型はありません。

レガシーシステムの海原へ漕ぎ出すあなたを、PL/Iのコンパイラもきっと温かく見守ってくれますよ。
日々の開発やマイグレーション作業、ぜひ楽しんで進めていってくださいね!

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