【テクニカル・上級編】FIXED BINARY(15)と(31)の内部表現 – PL/Iの基本構文とデータ制御実践ガイド

はじめに:なぜ今、`FIXED BINARY` の内部表現なのか

基幹システムの現場で20年、30年と稼働し続けているメインフレームのPL/Iプログラム。そのメンテナンスや、Java/C#へのマイグレーション(レガシー移行)の最前線に立つ私たちアーキテクトにとって、最も恐ろしいのは「動いているけれど、なぜ動いているのか誰も正確に説明できないコード」です。

特に、数値計算の根幹を支える `FIXED BINARY(15)` と `FIXED BINARY(31)` の扱いを誤ると、データ破損、予期せぬアベンド(S0C7はデシマルでお馴染みですが、算術系ではS0C1やS0C4、あるいはサイレントな計算ミス)、さらにはオープン系言語へ移行した際の「謎のオーバーフロー」というパンドラの箱を開けることになります。

今回は、PL/Iにおける2バイトおよび4バイト整数の内部表現と符号ビットの扱いを徹底的に解剖し、基幹システムの信頼性を守り抜くための実践知を共有します。

—

1. `FIXED BINARY` の内部構造:2バイトと4バイトの厳格な世界

PL/Iの `FIXED BINARY`(通称:F-Bin)は、IBM System/zのハードウェア命令(`AH`, `AL`, `C`, `CH`, `D`, `M`, `MR`, `S`, `SL`, `ST`, `MH` など)と直結した効率的な整数型です。

`FIXED BINARY(15)` の実態(2バイト / 半ワード)

  • 領域サイズ: 2バイト(16ビット / 半ワード)
  • 値の範囲: $-32,768$ から $+32,767$
  • 内部表現: 2の補数表現(Two’s complement)
  • 最高位ビット(Bit 0): 符号ビット(0 = 正またはゼロ、1 = 負)

`FIXED BINARY(31)` の実態(4バイト / フルワード)

  • 領域サイズ: 4バイト(32ビット / フルワード)
  • 値の範囲: $-2,147,483,648$ から $+2,147,483,647$
  • 内部表現: 2の補数表現
  • 最高位ビット(Bit 0): 符号ビット

ここで、C言語の `short` や `long`、Javaの `short` や `int` との決定的な違い、そしてPL/I特有の「精度(Precision)」の概念に注意しなければなりません。PL/Iではカッコ内の数字は「ビット数(符号を含まない有効桁数ではなく、全体の精度)」を指定します。したがって、`(15)` と指定すれば符号を含めてちょうど16ビット(2バイト)、`(31)` で32ビット(4バイト)が割り当てられます。

—

2. 予約語を持たない自由度の代償と、ベース変数・ポインタ操作の罠

PL/Iの言語仕様における最大の特異性は、「予約語(Reserved words)が存在しない」という点にあります。`IF` や `THEN` でさえも文脈キーワードであり、変数名として定義することが理論上可能です(推奨は絶対にしませんが)。

この柔軟性は、ベース変数(Based変数)とポインタを用いた動的ストレージ操作において、強力な武器となりますが、一歩間違えるとハードウェア例外(S0C4など)への直行便となります。

以下に、ストレイジ(ストレージ)上で `FIXED BINARY` がどのように並んでいるかを、ポインタ操作を伴う実践的なコードで確認します。

1
/ ————————————————– /
/ 外部ストレージや通信バッファからの動的アンパック処理 /
/ ————————————————– /
DCL RAW_BUFFER CHAR(1024) BASED(P_BUF);
DCL P_BUF POINTER;

/ 4バイト整数(FIXED BINARY(31))をマッピングする構造体 /
DCL 1 HEADER_AREA BASED(P_HEAD),
3 REC_ID FIXED BIN(15), / 2バイト: レコード識別子 /
3 REC_COUNT FIXED BIN(31); / 4バイト: レコード件数 /

DCL P_HEAD POINTER;

/ ポインタの割り当てとベース変数のアライメント /
P_HEAD = P_BUF;

/ 注意: ハードウェアアーキテクチャ上、FIXED BIN(31)(4バイト)は /
/ 4の倍数のアドレス(フルワード境界)に位置していることが望ましい。 /
/ 奇数アドレスからロードすると、古いアーキテクチャではS0C6が発生するか、 /
/ 現代のz/Architectureでもパフォーマンスが著しく低下する。 /

アーキテクチャの急所:境界アライメント(Boundary Alignment)

メインフレームのCPUは、2バイト整数は偶数アドレス、4バイト整数は4の倍数アドレスに配置されている(アライメントされている)ことを前提に最適化されています。
コンパイラオプションで `ATTRIBUTES` や `AGGREGATE` を指定しないまま、不整合なストラクチャを定義してバッチ処理を走らせると、サイレントな性能劣化、あるいは悪い場合にはデータ例外を引き起こします。

—

3. コンパイラオプションと最適化の罠:`TRUNC` の恐怖

マイグレーションやリビルドの際、最も多くのエンジニアがハマる落とし穴がコンパイラオプションの挙動差、特に `TRUNC`(Truncate) オプションです。

PL/Iの `FIXED BINARY` は、定義された精度を超える値を代入された場合、ハードウェアのレジスタ上では32ビットフルに計算が行われます。ここで `TRUNC(STD)`、`TRUNC(OPT)`、`TRUNC(BIN)` のどれを選択しているかによって、予期せぬ値の丸めやバグが発生します。

  • `TRUNC(STD)`(Standard): 宣言された精度(例: `FIXED BIN(15)` なら 15ビット)に合わせて、ストア時に厳密に切り捨てを行います。
  • `TRUNC(OPT)`(Optimize): パフォーマンスを最優先し、余分な切り捨て命令を生成しません。そのため、`FIXED BIN(15)` の領域に 32,767 を超える値(レジスタに残ったゴミなど)が入り込む可能性があります。
  • `TRUNC(BIN)`(Binary): 変数が定義されたサイズ(半ワードなら16ビット、フルワードなら32ビット)の最大範囲まで許容します。

> 【現場の教訓】
> オープン系言語(JavaやC#)へマイグレーションする際、移行先のシステムは厳密な型チェックとサイズ制限を持ちます。レガシー側で `TRUNC(OPT)` や `TRUNC(BIN)` に依存したコードが存在していた場合、Java側で `DataConversionException` や予期せぬマイナス値(符号ビットの誤認)が噴出します。移行前には必ずコンパイラリストで `TRUNC` 設定を確認し、アセスメントを行うべきです。

—

4. 埋め込みSQL(DB2)およびCICSオンラインにおけるエッジケース

基幹システムのオンライン(CICS)やデータベース(DB2)連携において、`FIXED BINARY` はその真価(と恐怖)を発揮します。

DB2の `SMALLINT` と `INTEGER` との厳密なマッピング

DB2の `SMALLINT` は2バイト整数であり、PL/Iの `FIXED BINARY(15)` と1:1で対応します。同様に `INTEGER` は4バイトであり、`FIXED BINARY(31)` と対応します。

しかし、ここで パックデシマル(`FIXED DECIMAL` / COMP-3) との混在による符号反転バグや変換オーバーヘッドが発生します。

1
/ DB2ホスト変数定義のアンチパターン例 /
Dcl 1 DB_RECORD,
3 EMP_ID_BIN FIXED BIN(31), / 正しい: INTEGER /
3 EMP_SALARY FIXED DEC(9,2); / パックデシマル /

/ オンライン画面(CICS 3270ストリーム)とのデータ受け渡し /
/ 画面から送られてきたテキストデータをFIXED BINに直接MOVEすると… /

CICSのセンド/レシーヴマップでは、数値は基本的にゾーンデシマル(DISPLAY)またはパックデシマルとして扱われます。これを `FIXED BINARY` に直結させる際、コンパイラの暗黙の型変換(Implicit Conversion)が走ります。
この変換時に、高位ビットのゴミや符号の解釈ミスにより、画面上の「+100」が内部で「-28672」化するような、極めてタチの悪いバグを生むことがあります。

—

5. アベンド(ABEND)発生時のダンプ解析:レジスタとストレージの読み方

夜間バッチが突然 `ASRA` (CICS) や `S0C7` / `S0CB` (Batch) で落ちたとき、システムアーキテクトとしての腕の見せ所です。特に `S0CB`(Fixed-point divide exception: ゼロ除算またはオーバーフロー)や `S0C1` は、`FIXED BINARY` の不正な値やポインタズレに起因することが多々あります。

ダンプリーディングの極意

1. PSW(Program Status Word)の確認:
アベンド発生時の命令アドレスを特定し、マシンインストラクション(例: `5A20 A004` などのRX形式命令)を逆アセンブルします。
2. 汎用レジスタ(GPR: General Purpose Registers)の目視:

  • GPR 0〜15の中に、怪しい 16進数が入っていないか確認します。
  • 例えば、`FIXED BIN(15)` の変数に格納されるべき領域に、符号ビットが立った状態で予期せぬフラグが混入していると、レジスタの上位ビット(GPRは32ビット全体)にゴミが残り、算術演算命令(`AR` や `MR`)でオーバーフローを起こします。

3. ストレージダンプ(Storage Dump)のピンポイント調査:

  • 問題の変数のアドレスをベースレジスタと変位(Displacement)から割り出し、16進ダンプで「H’8000’」(負の最大値)や不自然なパターンのバイト列になっていないかを突き止めます。

—

おわりに:レガシーの底流を支配する者へ

たかが「整数型」の2バイトと4バイト。しかし、この `FIXED BINARY(15)` と `FIXED BINARY(31)` の内部表現、アライメント、そしてコンパイラオプションの挙動を完全に手中に収めているかどうかが、基幹システムの安定稼働、そして安全かつ確実なマイグレーションを成功させるための決定的な分水嶺となります。

「動いているから触らない」のではなく、「なぜ動いているのかをハードウェアレベルで説明し尽くす」。それこそが、現代のメインフレームエンジニアおよび移行アーキテクトに求められる真のプロフェッショナルリズムです。

あなたの書くその一行のPL/Iコードが、今日も日本の社会インフラストラクチャの信頼を静かに、そして力強く支えています。

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