【入門編】PIC ‘S’符号付き数値の内部表現(ゾーン10進数とパック10進数) – PL/Iの基本構文とデータ制御実践ガイド

こんにちは!IBMメインフレームの世界へようこそ。
JavaやC#、あるいはCOBOLといったモダン、または少し前の業務系言語をご経験された方にとって、PL/I(Programming Language One)という名前を聞くだけで「なんだか難解そう」「古いレガシーの牙城だ」と身構えてしまうかもしれません。

でも、どうか安心してくださいね。どんなに歴史のあるメインフレームの技術であっても、メモリの裏側で何が起きているのかを一つずつ紐解いていけば、決して恐ろしいものではありません。むしろ、ハードウェアの動きをダイレクトに制御できる、非常にロジカルで美しい言語なんですよ。

今回は、そんなPL/Iのデータ制御における大きな山場の一つ、「PIC ‘S’ 符号付き数値の内部表現(ゾーン10進数とパック10進数)」について、じっくりと優しく解説していきます。実務でのバッチ改修や、オープン系へのマイグレーション調査で「おや?」とつまずきやすいポイントですので、ぜひ最後までお付き合いくださいね。

1. そもそもPL/Iの「数値」の持ち方は一筋縄ではいかない?

Javaなら `int` や `long`、COBOLなら `PIC S9(9) COMP` など、他の言語ではデータ型とその内部形式(バイナリなのか、10進数なのか)がセットで定義されることが多いですよね。

しかし、PL/Iの面白いところ(そして初学者が戸惑うところ)は、「人間が見る見た目の形(Picture)」「コンピュータがメモリ上でどう保持するか(Computational)」を、組み合わせで自由に指定できる点にあります。

特に、金額や数量といった「プラスもマイナスもある数値」を扱うときによく登場するのが、今回主役となる `PIC ‘S…’` という指定です。この `’S’` は Sign(符号) を意味しています。

2. ゾーン10進数(DISPLAY形式)の裏側:文字と数字の絶妙な関係

まずは、デフォルトの状態、あるいは `DISPLAY` と呼ばれる形式から見ていきましょう。
PL/Iで以下のように変数を宣言したとします。

1
DCL WS-AMOUNT PIC ‘S999’ ; / 3桁の符号付き数値(デフォルトはゾーン10進数) /

JavaやCのプログラマなら、「3桁ならせいぜい2〜4バイトのバイナリだろう」と思われるかもしれませんが、メインフレームの世界ではちょっと違います。この宣言の場合、メモリ上では3バイト(3文字分)を消費します。

「えっ、3桁なのに3バイト? 符号の『+』や『ー』はどこに消えたの?」と思いますよね。ここがメインフレーム(EBCDIC環境)のユニークなところです。

ゾーン10進数では、「1文字につき1バイト」を使います。

  • 上位4ビット(ゾーン部):文字か数字かを表す(数字の場合は通常 `1111`(HEXで `F`))
  • 下位4ビット(数値部):実際の数値を表す(例:`0` なら `0000`、`5` なら `0101`)

そして、一番最後の桁(右端のバイト)の「ゾーン部(上位4ビット)」が、符号の置き場所に変身するのです!

  • プラス(+)の場合: 最後のバイトのゾーン部が `1100`(HEX: `C`)になります。
  • マイナス(-)の場合: 最後のバイトのゾーン部が `1101`(HEX: `D`)になります。

例えば、変数に `-125` という値が入ったとき、メモリ上(EBCDIC)の16進数ダンプはこうなります。

> `F1 F2 D5`
> `F1` = 「1」(上位が F、下位が 1)
> `F2` = 「2」(上位が F、下位が 2)
> `D5` = 「5」+「マイナス符号」(上位がマイナスを意味する D、下位が 5)

人間が画面やプリンタでそのまま文字として読める(Displayできる)ので「ゾーン10進数(またはDISPLAY形式)」と呼ばれています。直感的ですが、1桁に1バイト使うため、メモリ効率や計算効率の面では少しもったいない形をしています。

3. パック10進数(COMP-3形式)の登場:メインフレームの省エネ技術

「もっとメモリを節約したい! 大量データを高速に計算したい!」
そうした現場の切実な要望に応えるのが、COBOLでお馴染みの `COMP-3`、PL/Iでは `COMPUTATIONAL-3`(または `COMP-3`) です。

先ほどの変数を、パック10進数として宣言してみましょう。

1
DCL WS-AMOUNT-COMP3 PIC ‘S999’ COMP-3 ; / パック10進数による宣言 /

この `COMP-3` を指定した瞬間、メモリの使われ方がガラリと変わります。
「1文字1バイト」という贅沢をやめ、「1バイトに数字を2つ(ぎゅっとパックして)」詰め込む形になります。これが「パック10進数」と呼ばれる理由です。

データの詰め込みルールと計算式

パック10進数のバイト数は、実はPictureの桁数(この場合は3桁)から自動的に計算されます。公式としては以下のようになります。

> 必要バイト数 = (桁数 + 2) ÷ 2 の小数点以下切り捨て

3桁なら `(3 + 2) / 2 = 2` で、なんとたったの2バイトに収まります!
1バイト目には「最高位の1桁(空きがあれば0で埋める)」が入り、2バイト目の前半に「最後の桁」、そして2バイト目の後半(右端の4ビット)に「符号」が陣取ります。

先ほどの `-125` を `COMP-3` で持たせた場合の、メモリ上の16進数ダンプを見てみましょう。

> `01 25 D` (※実際には2バイト分の `01 25` またはアライメントに依存しますが、ビットパターンのイメージです)
> `01` = 最初の桁「0」と次の桁「1」
> `2` = 3桁目の「2」
> `5` = 4桁目の「5」(に見えますが、実際は3桁なので、最後の1ニブルに「5」、最後の最後(右端)に符号の `D`(マイナス)が入ります)

※厳密な2バイトのビットマップ表現としては、`01 25` のうち、最後の `5` の部分が `5D`(下位4bitが5、一番右の4bitがD)となり、全体で `01 25`(HEX: `0125D` ではなく、2バイト=4ニブル+符号=合計5ニブル分を2バイトに綺麗に収めます。具体的には `01` と `25` または `12` と `5D` のように配置されます。おっと、細かいパッキングの割り算はコンパイラがよしなにやってくれるので安心してくださいね!)

符号を表すビット(ニブル)の定番は以下の通りです。

  • `C` = プラス(+)
  • `D` = マイナス(-)
  • `F` = 符号なし、または未定義(初期値など)

4. 実務で役立つ!PL/Iコード例と注意点

それでは、実際にPL/Iプログラムの中でこれらをどう扱うか、簡単なサンプルコードを見てみましょう。バッチプログラムの集計処理などをイメージしてみてください。

1
PGM1: PROC OPTIONS(MAIN);

/ 宣言部 /
DCL 1 W-DATA-AREA,
/ ゾーン10進数:画面入出力や外部ファイルとのI/Oでよく使います /
5 IN-VAL-DISPLAY PIC ‘S9999’ DISPLAY INIT(‘+1234’),

/ パック10進数:内部での計算や大量のマスター保持で大活躍します /
5 WK-VAL-COMP3 PIC ‘S9999’ COMP-3 INIT(0);

/ 処理部 /
/ ゾーン10進数のデータを、パック10進数に代入する /
WK-VAL-COMP3 = IN-VAL-DISPLAY;

/ 演算を行う(COMP-3同士ならメインフレームの専用命令で高速に処理されます) /
WK-VAL-COMP3 = WK-VAL-COMP3 + 500;

/ 結果をDISPLAY形式に戻して確認 /
PUT SKIP EDIT (‘計算結果(DISPLAY): ‘, IN-VAL-DISPLAY) (A, X(1), A);

RETURN;
END PGM1;

ここで知っておきたい実務の知見(トラブルシューティングのヒント)

レガシーシステムの移行や保守で最も多いトラブルの一つが、「外部から入ってきたデータ(ファイルや電文)の符号ビットが壊れていて、コンバージョンエラー(S0C7などのデータ例外)になる」という現象です。

もし、ゾーン10進数やパック10進数の右端の符号ニブルに、`C`、`D`、`F` 以外のゴミデータ(例えばアプリケーションのバグで文字コードが混ざった等)が入ると、メインフレームのCPUは「これ、数字として計算できないよ!」と悲鳴を上げ、アボート(異常終了)してしまいます。

もし「なんだか数値項目の演算で突然落ちるな……」という場面に遭遇したら、dumpリストやスナップショットを確認し、「変数の右端の4ビット(または2バイト目の右端)がちゃんと `C` や `D` になっているか」を疑ってみてください。原因究明の大きな近道になりますよ。

5. おわりに

いかがでしたでしょうか?
「PIC ‘S’」というシンプルな指定の裏側で、メモリの1バイト、さらには1バイトを半分コした「ニブル(4ビット)」単位で、符号と数字が仲良く同居している様子がイメージできたでしょうか。

一見すると古めかしく難解に見えるメインフレームのデータ表現ですが、突き詰めていくと「限られたメモリとCPU資源を極限まで効率よく使おう」という先人たちの知恵と工夫の結晶であることが分かります。

最初は怖く感じるレガシーコードも、こうして中身のビットパターンを覗いてあげれば、ただの「データという名のパズル」です。ぜひ、一つひとつの仕組みを楽しみながら、メインフレーム・マスターへの道を歩んでいってくださいね。あなたの開発ライフを、心から応援しています!

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