こんにちは!メインフレームの世界へようこそ。
JavaやCOBOLといった他のプログラミング言語の経験がある方にとって、突然目の前に現れるPL/I(ピーエルアイ)のソースコードは、まるで暗号のように見えて身構えてしまうかもしれませんよね。
「なんだこの見慣れない書き方は……」と不安になるかもしれませんが、大丈夫ですよ。一つずつ蓋を開けて中身を見ていけば、JavaやCOBOLと本質的な考え方は同じです。
今回は、PL/Iのデータ制御において避けて通れない、`FIXED BINARY(15)` と `FIXED BINARY(31)` という整数型の内部表現と、演算時のオーバーフローの秘密について、おしゃべりするような感覚で優しく紐解いていきましょう。
—
そもそもPL/Iの変数宣言って?(ちょっとしたトリビア)
Javaなら `int count;`、COBOLなら `01 COUNT PIC S9(4) COMP.` と書くところを、PL/Iでは次のように書きます。
1
DCL COUNT FIXED BIN(15);
「DCL」は Declare(宣言)の略です。そしてPL/Iの面白いところ(あるいは驚くところ)は、「予約語を持たない」という懐の深さを持っている点です。つまり、もしあなたが `FIXED` や `BINARY` という単語をうっかり変数名に使ったとしても、コンパイラは前後の文脈から「あ、これは変数名だな」「ここはキーワードだな」と賢く判断してくれます(※もちろん、混乱の元なのでわざわざそんな命名はしませんが!)。
さて、この `FIXED BINARY`、一体中でどうなっているのでしょうか?
—
1. ハーフワードとフルワードの正体
Javaの `short` や `int`、あるいはCOBOLの `COMP`(バイナリ項目)と同じ仲間だと思ってください。
`FIXED BINARY(15)` —— ハーフワード整数(16ビット)
- サイズ: 2バイト(16ビット)
- 表現できる範囲: $-32,768$ から $+32,767$ まで
- イメージ: 小さな箱。カウンターや、あまり大きくならないコード値などに使われます。
`FIXED BINARY(31)` —— フルワード整数(32ビット)
- サイズ: 4バイト(32ビット)
- 表現できる範囲: $-2,147,483,648$ から $+2,147,483,643$ まで
- イメージ: 中くらいの箱。通常の件数カウントや金額(円単位など)でよく使われます。
> 💡 メインフレーム・アーキテクチャ(S/370など)の裏話
> IBMの汎用機(メインフレーム)のCPUは、ハードウェアの命令セットとして「2バイト単位(ハーフワード)」と「4バイト単位(フルワード)」の演算を非常に得意としています。括弧の中の数字 `(15)` や `(31)` は、「符号部(プラスマイナス)を除いた有効ビット数」を表しています。
> つまり、`15` ビットの数字データ + `1` ビットの符号 = 合計16ビット(2バイト)というわけです。JavaやCOBOLに慣れていると「なんで16じゃなくて15なの?」と戸惑いますが、この「ビット数指定」がPL/Iの厳密で美しいところです。
—
2. 実際にコードで見てみましょう
それでは、実際のPL/Iのプログラム片を見てみましょう。大文字で書かれているのがレガシーっぽくてカッコいいですよね。
1
//
/ 修正バイナリ型の演算とオーバーフローのサンプル /
//
DEMO_PROG: PROC OPTIONS(MAIN);
/ 変数宣言 /
DCL WS_SMALL_CNT FIXED BIN(15) INIT(0); / ハーフワード変数 /
DCL WS_BIG_CNT FIXED BIN(31) INIT(0); / フルワード変数 /
DCL WS_LIMIT FIXED BIN(15) INIT(32767); / 最大値 /
/ 1. ハーフワードの限界値に加算してみる /
WS_SMALL_CNT = WS_LIMIT;
PUT SKIP LIST(‘現在のSMALL値:’, WS_SMALL_CNT);
/ ここでさらに ‘1’ を足すとどうなるか? /
/ WS_SMALL_CNT = WS_SMALL_CNT + 1; <-- ここでオーバーフロー! /
END DEMO_PROG;
---
3. 恐怖の「オーバーフロー」発生条件とPL/Iの挙動
さて、ここからが本題です。もし上記のコードで `WS_SMALL_CNT`(最大値 32,767)に `1` を足したらどうなるでしょうか?
JavaやC言語であれば、上位ビットがあふれてマイナス方向に値が反転したり(ラップアラウンド)、処理系によってはサイレントにバグを埋め込んだりします。
しかし、PL/Iの世界(特にデフォルトのコンパイラオプション)では、ハードウェア割り込みが発生し、コンディション(例外)が送出されます。
演算時の注意点
1. 精度の自動昇格(中間結果の魔術)
PL/Iは非常に頭の良い言語です。例えば、`FIXED BIN(15)` 同士を足し算する際、コンパイラは内部的に一時領域としてより大きな精度(通常は `FIXED BIN(31)` やそれ以上)を使って計算を行います。そのため、単純な足し算の途中ですぐにあふれることはありません。
2. 代入時の悲劇
問題は、計算結果を元の小さな変数(例えば `FIXED BIN(15)`)に代入する瞬間です。結果が `32,767` を超えていれば、その瞬間に `FIXEDOVERFLOW`(略して `FOFL`)というシステム例外が発生します。
3. 対策はどうするの?
もし大きな数値を扱う可能性があるなら、ケチらずに最初から `FIXED BIN(31)` や、さらに大きな `FIXED BIN(63)`(ダブルワード)で宣言しておけば安心です。
万が一、意図しないオーバーフローでバッチジョブが異常終了(ABEND: ユーザーコードやシステムコード `S0C7` や `OC4`、あるいはPL/I特有のABEND)してしまったときは、大抵このデータ型のサイズ不足か、初期化漏れが原因です。
—
まとめ
- `FIXED BIN(15)` は 2バイト(最大約3.2万)のハーフワード。
- `FIXED BIN(31)` は 4バイト(最大約21億)のフルワード。
- 枠に入り切らないデータを無理やり代入しようとすると、PL/Iは容赦なくオーバーフローのシグナルを上げます。
「レガシーの型って厳格すぎて怖いな……」と思われるかもしれませんが、これは裏を返すと「メモリやデータの整合性を極限までカチッと守ってくれる信頼性の高さ」の表れです。
マイグレーションや保守でPL/Iに直面したときは、「あ、今この変数は何桁の箱に入ろうとしているんだっけ?」と、変数の「箱の大きさ」を意識してあげてくださいね。それだけで、コードの読みやすさが劇的に変わりますよ!
それでは、快適なメインフレームライフを!
