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

こんにちは!メインフレームの世界へようこそ。
JavaやC#、あるいはCOBOLなどのモダンな(あるいは少し毛色の違う)言語をバリバリ書いてきた方にとって、IBMメインフレームの世界、そして「PL/I(ピーエルワン)」という言語は、最初は少し堅苦しく、時には謎の呪文のように見えるかもしれませんよね。

「なんだか古い言語だし、データ型も難しそう…」
「間違えたらメインフレームが止まったりしないかな…」

そんな不安を抱えていませんか? 大丈夫です、怖がる必要は全くありません!今回は、PL/Iの数あるデータ型の中でも、金融や基幹システムの心臓部で必ずと言っていいほど使われる「FIXED DECIMAL(p,q)(いわゆるパック10進数 / COMP-3)」について、実務の現場の視点を交えながら、優しく、そして深く紐解いていきましょう。

—

1. そもそも「パック10進数」ってなに?(他言語との違い)

Javaの `int` や `long`、あるいはC言語の数値型に慣れていると、メモリ上の数字は「2進数(バイナリ)」で表現されるのが当たり前だと思います。

しかし、お金を扱う銀行の勘定系システムや、1円の狂いも許されない基幹システムでは、2進数特有の「小数点の誤差(丸め誤差)」は致命傷になります。例えば、`0.1 + 0.2` を計算したら `0.30000000000000004` になっちゃった!……なんて笑い話は、メインフレームの世界では許されないわけです。

そこで登場するのが、人間が普段使う「10進数」のままメモリ上にデータを保持する方式です。その代表格が、COBOLの `COMP-3` でもおなじみの「パック10進数」です。

パック10進数のイメージ:1バイトに数字が「2つ」入る?

PL/Iでは、これを `FIXED DECIMAL(p, q)` という属性で宣言します。

  • `p` (Precision / 精度):数字全体の桁数(符号を含まない)
  • `q` (Scale / 尺度):小数点の位置(右から何桁目が小数部か)

例えば、`FIXED DECIMAL(5, 2)` と書いたら、「全体で5桁、うち小数点が下2桁」という意味になります。具体的には `123.45` のような値が入りますね。

このデータがメモリ上でどう格納されているかというと、1バイトの中に、なんと数字が「2つ」キュキュッと詰め込まれています(これを「ニブル(4ビット)」単位の分割と呼びます)。

例:数値 「+123.45」 をメモリ(3バイト)に格納する場合
[ 0123 ] [ 45 ] [ 1C ]
↑ ↑ ↑
整数部 小数部 符号ニブル(Cはプラス、Dはマイナス、Fは符号なし)

一番最後のバイトの後半(右側)の4ビットには、「符号(プラスかマイナスか)」が入ります。
「C」ならプラス(Plus)、「D」ならマイナス(Minus)です。昔のエンジニアの暗号のようですが、慣れるとなかなか愛嬌のある構造ですよね。

—

2. PL/Iでの実際の書き方を見てみましょう

百聞は一見に如かず。実際にPL/Iでどのように変数を定義し、計算するのか、サンプルコードを見てみましょう。大文字で書くのがメインフレーム流の伝統です。

1
————————————————————–

  • パック10進数(FIXED DECIMAL)の宣言と演算のサンプル

————————————————————–
TESTPROG: PROC OPTIONS(MAIN);

— 変数の宣言 —

  • 5桁(小数2桁)のパック10進数として宣言します(COBOLの COMP-3 相当)

DCL WK-TANKA FIXED DECIMAL(5,2) INIT(123.45);
DCL WK-SURYO FIXED DECIMAL(3,0) INIT(10);
DCL WK-KINGAKU FIXED DECIMAL(8,2);

— 算術演算の実行 —

  • 単価 × 数量 を計算し、金額に代入します

WK-KINGAKU = WK-TANKA WK-SURYO;

— 結果の出力 —
PUT SKIP LIST (‘単価 : ‘, WK-TANKA);
PUT SKIP LIST (‘数量 : ‘, WK-SURYO);
PUT SKIP LIST (‘金額 : ‘, WK-KINGAKU);

END TESTPROG;

どうでしょう? コード自体は非常に直感的ですよね。
`INIT(123.45)` のように初期値を書くこともできますし、通常の四則演算子(“, `/`, `+`, `-`)をそのまま使うことができます。

—

3. ここが重要!「演算時の変換コスト」の罠

さて、ここからがシステムアーキテクトとしての腕の見せ所、そして現場でありがちなパフォーマンス・トラブルの話です。

PL/Iは非常に懐の深い言語で、異なるデータ型同士を計算させようとすると、コンパイラが勝手にイイ感じに型変換(暗黙の型変換)を行ってくれます。例えば、FIXED DECIMAL と、通常の固定小数点2進数(`FIXED BINARY`)を足し算したりしても、エラーにならずに計算できちゃいます。

「おっ、賢いじゃん!便利!」と思いますよね?
……ここに、レガシーシステムのパフォーマンスを落とし込む巧妙な罠があります。

2進数と10進数の「文化の違い」

  • FIXED BINARY (COMP):CPUが直接得意とする「2進数」の世界(瞬時に計算できる)
  • FIXED DECIMAL (COMP-3):メモリ上では10進数だが、CPUで計算するためには、内部でわざわざ「2進数に直すか、専用の10進数演算命令(Decimal Arithmetic Instructions)を呼び出す」必要がある。

もし、性能がシビアに求められる大量データのバッチ処理のループ内で、次のようなコードを書いたらどうなるでしょう?

1

  • 悪い例:型の異なる変数同士の演算がループ内で発生

DO I = 1 TO 1000000;

  • I は FIXED BINARY(31) (通常デフォルトのループカウンタ)
  • WK-KINGAKU は FIXED DECIMAL(8,2)

TOTAL = TOTAL + (PRICE I); 毎回、型変換のオーバーヘッドが発生!
END;

CPUは「あれ?こっちはバイナリ、こっちはデシマルだから、計算する前に変換しなきゃ……」と、裏でこっそり余計な仕事(データ型のキャストと内部フォーマットの変換)をさせられます。
これが100万回、1000万回と積み重なると、バッチ処理の実行時間が数分〜数十分も遅延する原因になります。

アーキテクトからのアドバイス

基幹システムのマイグレーションやパフォーマンスチューニングを行う際は、以下のルールを徹底してみてください。

1. 計算に関わる変数の属性(データ型・精度・小数位)を極力そろえる。
2. 内部で頻繁に使うカウンターやワーク変数は `FIXED BINARY` で統一し、金額や数量など「画面や帳票にそのまま出す、あるいはDBに直結するデータ」のみを `FIXED DECIMAL` にする。
3. コンパイルリスト(Cross-reference / 属性リスト)を必ず確認し、意図しない暗黙の型変換(Compiler Diagnostic Messages)が発生していないかチェックする。

—

4. おわりに:怖がらなくて大丈夫です

PL/Iの `FIXED DECIMAL(p,q)` は、最初は「ニブル?符号?」と独特の仕様にたじろいでしまうかもしれませんが、その実体は「人間のビジネス計算の精度を完璧に守るための、極めて合理的で優しさに満ちたデータ構造」です。

JavaやCOBOLの経験があるあなたなら、データが持つ「桁数」と「小数点の位置」の概念さえ掴んでしまえば、すぐに使いこなせるようになります。

レガシーシステムの海原へ漕ぎ出すあなたの背中を、この記事が少しでも押すことができたなら幸いです。
現場でのトラブルシューティングや移行作業、応援していますよ!それではまた次の技術でお会いしましょう。

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