【実務・中級編】FIXED BINARY(15)とFIXED BINARY(31)の内部表現と精度 – PL/Iの基本構文とデータ制御実践ガイド

おい、最近アホみたいに発生している夜間バッチの「S0C7」や、原因不明の数値データ切り捨てエラーに頭を悩ませていないか?

「いやぁ先輩、COBOLならCOMP-3とかで慣れてるんですけど、PL/Iのバイナリ演算ってどうも裏側の挙動が掴めなくて……」なんて言ってるうちは、まだまだ基幹システムの荒波を生き抜けんぞ。

特に`FIXED BINARY(15)`と`FIXED BINARY(31)`。この2つの違いを「ハーフワードかフルワードかでしょ?」程度に捉えていると、ある日突然、大規模な金融データの突合で致命的な桁落ちやオーバーフローを引き起こし、夜中の3時に運用保守の鬼コーチから赤電話を受けるハメになる。

今回は、PL/Iにおける整数バイナリ表現の真髄と、VSAMやレコード入出力時の罠、そして鉄壁のONユニット制御について、現場の泥臭い知見を交えて徹底的に叩き込んでやる。心して読め。

1. 内部表現のメカニズム:なぜ`FIXED BINARY`なのか

まず大前提として、PL/IにはCOBOLのような「予約語の縛り」が極めて少ない。そのため、変数名にうっかり言語仕様のキーワードを使い込んで自爆するようなアホなミスは起きにくいが、その代わりデータ属性の指定ミスがそのままコンパイラの最適化やハードウェア命令(System zのアーキテクチャ)に直結する

ハーフワード `FIXED BINARY(15)` の世界

  • 占有領域: 2バイト(16ビット)
  • 表現範囲: $-32,768$ 〜 $+32,767$ (符号付き2の補数表現)
  • 主な用途: ループカウンタ、小さなフラグ、配列のインデックスなど、メモリを極限まで節約したい領域。

フルワード `FIXED BINARY(31)` の世界

  • 占有領域: 4バイト(32ビット)
  • 表現範囲: $-2,147,483,648$ 〜 $+2,147,483,647$
  • 主な用途: 金額の整数部、トランザクション件数、シーケンス番号など、実務で扱うほとんどの数値演算。

ここでベテランとしての最初の教訓だ。
「とりあえずメモリ食わなそうだから全部(15)にしておけ」という若手の発想は、今すぐゴミ箱に捨てろ。

System z(z/Architecture)のCPUは、32ビットレジスタをベースに最適化されている。中途半端に`FIXED BINARY(15)`同士で演算をさせると、コンパイラはレジスタの上下半分をマスク処理する命令を余分に吐くことがある。パフォーマンスの観点でも、現代のメインフレームでは基本は`FIXED BINARY(31)`、あるいは計算効率の良い`FIXED DECIMAL`を適切に使い分けるのが鉄則だ。

2. 演算時のオーバーフロー発生条件とコンパイラの罠

PL/Iの恐ろしいところ(そして強力なところ)は、「暗黙の型変換(Promotion)」「中間結果の精度保持」のルールにある。

例えば、次のような演算を考えてみてほしい。

DCL A FIXED BINARY(15) INIT(30000);
DCL B FIXED BINARY(15) INIT(30000);
DCL C FIXED BINARY(31);

C = A + B;

一見、`C`はフルワードの`FIXED BINARY(31)`だから余裕で受け止められそうに見えるだろ?
しかし、PL/Iの言語仕様では、「演算の中間結果の精度は、オペランドの属性に基づいて決定される」
`FIXED BINARY(15)` 同士の加算であれば、中間結果も自動的に`FIXED BINARY(15)`として計算されようとする。その結果、計算直後に上限値の32,767を超えた瞬間にハードウェア割り込み(S0C7やS0C1、あるいはコンディションの発生)を引き起こすか、最悪の場合は静かに上位ビットが切り捨てられて異次元のマイナス値が`C`に流れ込むことになる。

これを防ぐためには、明示的にキャストするか、最初から適切な属性で変数を定義しておく必要があるのだ。

3. レコード入出力とVSAMアクセスにおける注意点

メインフレームのバッチ処理において、VSAM(KSDSやESDS)やQSAMのレコードレイアウトを定義する際、COBOLの`COPY`句に相当するのがPL/Iの`INCLUDE`(またはストラクチャ定義)だ。

ここでCOBOL上がりのエンジニアがよくやるミスが、外部ファイル(COBOLプログラムが書いたデータや、古い世代の磁気テープ)のバイナリ項目の扱いだ。COBOLの`COMP`(フルワード)や`COMP-4`は、PL/Iの`FIXED BINARY(31)`と完全に一致するが、ビッグエンディアン(IBM 370アーキテクチャ)のバイトオーダーの意識が抜けていると、マイグレーション時に痛い目を見る。

特に、C言語やオープン系から流れてきたリトルエンディアンのデータを扱う場合、PL/I側でそのまま`FIXED BINARY`として読み込むと値が完全に化ける。メインフレーム内完結のバッチであれば問題ないが、外部連携すらないか? 今一度、データ定義(DCL)を疑え。

4. 実践コード:ONユニットによる堅牢なエラー制御

さあ、理屈はここまでだ。実際の現場で使える、堅牢なエラーハンドリングを組み込んだPL/Iのサンプルコードを見せてやろう。

このコードは、`FIXED BINARY(15)`の限界値を超える演算を行った際に、プログラムが異常終了(ABEND)するのを防ぎ、`ON CONVERSION`や`ON OVERFLOW`のコンディション・ハンドラでトラップして安全に処理を継続・ログ出力する実戦的なパターンだ。

—————————————————————-

  • プログラム名: FBINTEST
  • 概要 : FIXED BINARYの精度検証とONユニット制御サンプル

—————————————————————-
FBINTEST: PROC OPTIONS(MAIN);

/ 変数宣言 /
DCL WS-VAL-1 FIXED BINARY(15) INIT(30000);
DCL WS-VAL-2 FIXED BINARY(15) INIT(5000);
DCL WS-RESULT-1 FIXED BINARY(15);
DCL WS-RESULT-2 FIXED BINARY(31);
DCL WS-MSG CHAR(80);

/ オーバーフローコンディションの捕捉 /
ON OVERFLOW
BEGIN;
PUT SKIP EDIT (‘【警告】オーバーフローを検出しました。’
)(A);
/ 安全なデフォルト値にフォールバック /
WS-RESULT-1 = 0;
GOTO ERROR_BUMP;
END;

PUT SKIP EDIT (‘=== FIXED BINARY 演算テスト開始 ===’)(A);

/ 1. ハーフワード同士の危険な足し算 (30000 + 5000 = 35000 > 32767) /
/ ここでOVERFLOWコンディションが発動する /
WS-RESULT-1 = WS-VAL-1 + WS-VAL-2;

PUT SKIP EDIT (‘結果1(15bit): ‘, WS-RESULT-1)(A, F(10));

ERROR_BUMP:

/ 2. フルワード(31)へ正しく拡張した安全な演算 /
WS-RESULT-2 = BIN(WS-VAL-1, 31) + BIN(WS-VAL-2, 31);

PUT SKIP EDIT (‘結果2(31bit・安全): ‘, WS-RESULT-2)(A, F(10));

/ ビルトイン関数を用いた最大値・最小値の確認 /
PUT SKIP EDIT (‘HIGH(WS-RESULT-2): ‘, HIGH(WS-RESULT-2))(A);

PUT SKIP EDIT (‘=== FIXED BINARY 演算テスト終了 ===’)(A);

RETURN;

END FBINTEST;

コードの解説とデバッグのコツ

1. `ON OVERFLOW` ユニットの配置:
PL/Iの強力な武器がこの「ONユニット」だ。COBOLの`ON SIZE ERROR`は個別命令ごとに書く必要があるが、PL/Iではブロック有効範囲内の算術オーバーフローをグローバル、かつスマートに捕捉できる。
2. `BIN(変数, 31)` ビルトイン関数:
計算の途中で明示的に精度を拡張(Promotion)させるために、`BIN`ビルトイン関数を使いこなせ。これだけで予期せぬ桁落ちやオーバーフローの大部分を防ぐことができる。
3. ビルトイン関数の積極的活用:
PL/Iには`HIGH()`や`LOW()`、あるいは文字操作・数値変換の強力なビルトイン関数が標準で備わっている。車輪の再発明をするな。マニュアルの「言語リファレンス」のビルトイン関数の章は、寝る前に枕元において愛読すべきバイブルだ。

5. まとめとして:レガシーエンジニアの誇り

メインフレームのコードは、時に「古臭い遺物」のように言われることがある。しかし、何十年も止まることなく企業の基幹データを支え続けてきたその裏側には、数学的な整合性とハードウェアの限界を見据え抜いた先人たちの知恵が詰まっているのだ。

`FIXED BINARY(15)`と`(31)`の選択ひとつとっても、CPUのレジスタ効率、メモリ、そしてオーバーフローのリスクヘッジという、システムアーキテクトとしての思想が反映される。

次にバッチの改修を任されたとき、「動けばいいや」ではなく、「なぜこの属性なのか」「この演算で中間バッファは溢れないか」を自問自答してほしい。お前ならやれる。何か詰まったら、いつでも俺のところに聞きに来い。

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