こんにちは!メインフレームの世界へようこそ。
JavaやCOBOLといった他のモダンな言語や基幹系言語の経験がある方にとって、初めて目にするPL/I(ピーエルアイ)のソースコードは、どこか独特で、少し古い要塞の扉を開けるような緊張感があるかもしれませんよね。
特に、データ定義のセクションに現れる `FIXED BINARY(15)` といった見慣れない記述に出会うと、「これっていったい何桁の数字が入るんだろ…?」と手が止まってしまうこともしばしば。
でも、安心してください。怖がる必要は全くありません!今回は、PL/Iの数あるデータ型の中でも、計算処理の主役を務める `FIXED BINARY`(固定小数点2進数型)の内部表現と、精度(P, Q)の制約 について、他の言語との違いを交えながら、そっと優しく紐解いていきましょう。
—
1. 他の言語のエンジニアが驚く、PL/Iの「予約語レス」な世界
まず、中身に入る前にPL/Iのちょっとユニークな性格をお話しさせてください。
JavaやCOBOLでは、`if` や `class`、`DISPLAY` といったキーワードは「予約語」として厳格に保護されており、変数名に使うことはできませんよね。
ところが、PL/Iには「言語としての予約語が存在しない」という驚きの仕様があります。
どういうことかと言うと、極端な話、コンパイラは前後の文脈(コンテキスト)から「あ、これは変数名だな」「ここは命令文だな」と空気を読んで判断してくれます。そのため、以下のような少しスリリングな変数宣言も構文上はエラーになりません。
1
/ PL/Iでは予約語がないため、こんな強気な変数名も許されてしまいます /
DCL IF FIXED BIN(31); / 変数名が「IF」! /
※実務でこんな名前をつけると、後からコードを読む同僚のメンタルを破壊することになるので絶対にやめましょうね(笑)。でも、この「懐の深さ」がPL/Iという言語のベースにあることを知っておくだけで、コードを見る目が少し優しくなれるはずです。
—
2. 本題:`FIXED BINARY` って一体なに?
さて、本題の `FIXED BINARY`(省略して `FIXED BIN` と書くことも多いです)です。これは一言で言うと、「小数点のない整数(小数点は固定、つまり整数)」を「2進数(バイナリ)」で保持するデータ型です。
COBOLでいう `COMP`(コンピュテーション・フィールド)や、C言語の `short` や `long`、Javaの `int` や `long` に相当します。メインフレームのCPU(IBM Z)は、内部で2進数演算を高速に行うため、数値をガンガン計算するループのカウンタや金額のIDなどの処理には、この `FIXED BINARY` が飛ぶように使われます。
内部表現の仕組み:2の補数と「ビットの世界」
コンピュータの心臓部は、突き詰めれば「電気のON/OFF(0と1)」の集まりです。`FIXED BINARY` は、指定されたビット数を使って数値を表現します。
マイナスの数値をどう扱うかと言うと、これも他の言語でお馴染みの 「2の補数(Two’s complement)」 という方式をとっています。
- 一番左端のビット(符号ビット)が `0` なら「プラス」
- 一番左端のビットが `1` なら「マイナス」
例えば、1バイト(8ビット)の世界で考えてみましょう。「5」という数字は、2進数で `00000101` と表現されますよね。では、「-5」はどうなるでしょうか? すべてのビットを反転させて(1の補数)、最後に `1` を足す(2の補数)というお馴染みのルールによって、コンピュータの内部でしっかりと管理されています。私たちが手計算で意識する必要は普段ほとんどありませんが、「あぁ、裏側ではCPUが理解しやすい純粋な2進数でキビキビ動いてるんだな」とイメージできればバッチリです。
—
3. 精度(P, Q)の制約と、知っておくべき「15」と「31」の壁
ここからが今回の最も重要なポイント、精度(Precision)の指定です。
PL/Iでは、変数を宣言する `DCL`(DECLARE)文の中で、カッコ書きでビット数を指定します。これが `FIXED BIN(P)` の P です。
1
Dcl 従業員コード Fixed Bin(15); / 半ワード(16ビット)整数 /
Dcl 総売上金額 Fixed Bin(31); / フルワード(32ビット)整数 /
ここで、COBOLやJavaから来た人が「おや?」とつまずきやすいポイントがあります。
そう、カッコの中の数字は 「10進数の桁数」ではなく、「2進数のビット数」 なのです!
IBMメインフレームのアーキテクチャ(文字通りハードウェアの設計)において、整数を効率よく処理するために、よく使われるビット数は決まっています。
① FIXED BIN(15) (半ワード / Halfword)
- 使えるビット数: 15ビット + 符号1ビット = 合計16ビット(2バイト)
- 表現できる数値の範囲: `-32,768` から `+32,767` まで
- 利用シーン: ちょっとしたループのカウンタや、小さなコード値など。Javaの `short` 型と同じです。
② FIXED BIN(31) (フルワード / Fullword)
- 使えるビット数: 31ビット + 符号1ビット = 合計32ビット(4バイト)
- 表現できる数値の範囲: `-2,147,483,648` から `+2,147,483,647` まで
- 利用シーン: 基幹システムのIDや、少し大きめの件数・金額など。Javaの `int` 型、COBOLの `PIC S9(9) COMP` に相当します。
※もし、さらに巨大な数値を扱う場合は `FIXED BIN(63)`(ダブルワード / 8バイト)を使うこともできます。
—
4. コンパイラオプションの罠とデフォルトの恐怖
ここで、レガシー移行の現場で本当によくある「おそろしい罠」のお話をします。
もし、あなたが以下のように、ビット数を省略してうっかり宣言してしまったらどうなるでしょうか?
1
Dcl 謎の変数 Fixed Bin; / ビット数を省略している! /
「あれ?エラーにならないの?」と思いますよね。エラーにはなりません。
しかし、ここがPL/Iのコンパイラオプションの怖いところです。PL/Iには、コンパイル時のオプション(`FIXEDDEC` や `LIMIT` など、あるいはデフォルトの挙動)によって、ビット数を省略したときのデフォルトの精度が変わります。
ある環境やコンパイラ設定では `FIXED BIN(15)` とみなされ、別のモジュールや移植物などでは `FIXED BIN(31)` と解釈されることがあります。
もし、意図せず `FIXED BIN(15)` になってしまった変数に、うっかり「50,000」という数値を代入するとどうなるでしょうか?
—
5. オーバーフロー発生時の挙動:知られざる「サイレント切捨て」の恐怖
Javaなどのモダン言語であれば、範囲外の値を入れようとすると `ArithmeticException`(例外)がスローされてプログラムがビタッと止まりますよね。COBOLでも、設定によってはサイズエラーやABEND(異常終了)になります。
しかし、PL/Iのデフォルトの算術演算において、オーバーフローが発生したときの挙動は…時として「何食わぬ顔で上位ビットを切り捨てて計算を続行する(サイレント・オーバーフロー)」ことがあります。
「あれ? さっきまで正しかった集計結果が、急にマイナスになったり変な数字に化けてるぞ…?」
というバグの調査に入ると、大体こういうデータ型の宣言漏れや、意図しない暗黙の型変換(プロモーション)が隠れています。
だからこそ、私たちシステムアーキテクトからの現場の鉄則として、以下のプラクティスを強くおすすめします。
> 💡 実務でのアーキテクチャ指針
> `FIXED BIN` を使うときは、絶対にビット数(15 や 31 など)を省略しない!
> 「足りなくなるかも」と不安になるくらいなら、基本的には余裕を持たせて `FIXED BIN(31)` を選んでおくのが無難です(メインフレームの現代的なCPU性能を考えれば、16ビットと32ビットの処理速度の差は、現代のシステムではほとんどボトルネックになりません)。
—
6. 実用的なPL/Iコード例:安全なデータ定義と演算
それでは最後に、実際に基幹システムのバッチプログラムなどで見かける、綺麗で安全な `FIXED BINARY` の使用例を見てみましょう。
1
SAMPLE: PROC OPTIONS(MAIN);
/————————————————–
- 丁寧で安全なデータ宣言のサンプル
————————————————–/
DCL W_LOOP_CNT FIXED BIN(15) INIT(0); / 2バイト:ループカウンタ用(小規模) /
DCL W_TOTAL_QTY FIXED BIN(31) INIT(0); / 4バイト:合計数量用(標準的な整数) /
DCL W_UNIT_PRICE FIXED DEC(7,2) INIT(0); / 比較用の10進数(固定小数点) /
/ ループ処理の例 /
DO W_LOOP_CNT = 1 TO 100;
/ 数量を加算していく /
W_TOTAL_QTY = W_TOTAL_QTY + 1000;
END;
/ 結果の出力(メインフレームのお馴染みSYSPRINT) /
PUT SKIP EDIT (‘累計数量: ‘, W_TOTAL_QTY)
(A(10), F(10));
END SAMPLE;
このコードでは、カウンタには `FIXED BIN(15)` を、合計値には少し余裕を持たせて `FIXED BIN(31)` を明示的に指定しています。これならコンパイラの環境に依存せず、意図した通りの安全なストレージと演算が保証されます。
—
まとめ
いかがでしたでしょうか?
- `FIXED BINARY` は2進数で高速に計算するための整数型であること。
- カッコ内の数字(P)は10進桁数ではなく「ビット数」であること(15なら約3万、31なら約21億まで)。
- ビット数を省略すると環境によって挙動が変わるリスクがあるため、必ず `(15)` や `(31)` を明示すること。
レガシーなPL/Iの世界は、一見すると難解な呪文のようですが、一つひとつの仕様の「理由」を紐解いていくと、当時のエンジニアたちの「ハードウェアの限界を極限まで引き出して高速に動かしたい!」という熱い工夫の歴史が見えてきて、なんだか少し愛おしくなってきませんか?
怖がる必要は全くありません。あなたのこれまでのプログラミング経験という素晴らしい武器があれば、PL/Iのデータ構造もすぐにマスターできますよ。
日々のメインフレーム開発や移行作業、応援しています!
