【実務・中級編】ビルトイン関数 BINARY, DECIMAL, FLOAT による明示的変換 – PL/Iの基本構文とデータ制御実践ガイド

おい、最近デバッグの途中で「なんでこんなところでデータ例外(S0C7)が起きるんだ…?」と頭を抱えていないか?

メインフレームの基幹システムで長年動いているPL/Iプログラムの改修をしていると、前任者が書いた泥臭いコードに出会うことが多い。特に、DECIMAL(固定小数点、いわゆるパック十進数)とBINARY(二進数)、そしてFLOAT(浮動小数点)が入り交じった演算で、暗黙の型変換(Implicit Conversion)に足をすくわれるトラブルは、レガシー開発の現場では「あるある」の悪夢だ。

PL/Iという言語は、変数名に予約語がない(キーワードの文脈依存性がある)という変態的な…いや、懐の深い仕様を持っている。それゆえに、書き手がいい加減なコードを書くと、コンパイラが勝手に気を利かせて型変換を行い、夜間バッチの土壇場で桁あふれや精度落ちを引き起こす。

今回は、この「暗黙の型変換」という魔物に頼らず、プログラマが意図した通りに型と精度をコントロールするためのビルトイン関数 `BINARY`、`DECIMAL`、`FLOAT` の実戦的な使い方を、VSAMファイルの入出力やONユニットのトラップ制御を交えて徹底的に叩き込んでやろう。

—

1. なぜ「暗黙の型変換」は現場の地雷なのか?

PL/Iは非常に寛容な言語だ。異なるデータ型の間で演算を行おうとすると、コンパイラが自動的に型を合わせに行ってくれる。一見すると親切だが、これが大規模バッチの現場では「サイレントキラー」になる。

例えば、VSAMファイルから読み込んだ外部形式の数値(DISPLAY形式など)や、外部システムから連携されたパック十進数(COMP-3)の領域を、内部の計算用レジスタ(BINARY)に落とし込む際、桁数や精度の定義を曖昧にしたまま演算させるとどうなるか。
コンパイラは勝手に丸め誤差を生じさせたり、最悪の場合、データ例外(S0C7)やサイズエラー(ON-conditionの `SIZE`)を引き起こしてジョブを異常終了させる。

プロなら、コンパイラの「お節介」に頼るな。「どのタイミングで、どのような精度(Precision)で型を変換するか」を、ビルトイン関数を使ってコード上で明示的に支配するのだ。これができるかどうかで、メインフレームエンジニアとしての寿命が決まる。

—

2. 3つの型変換ビルトイン関数の仕様と勘所

明示的変換を行うための主役は以下の3つだ。すべて `BUILTIN` 属性を明示して使うのが、我がチームのコーディング標準である。

1. `BINARY(expression [, p [, q]])`

  • 表現を二進数(FIXED BINARY または FLOAT BINARY)に変換する。
  • $p$ は全体のビット数(または精度)、$q$ は小数点位置。
  • 勘所:固定小数点演算のパフォーマンスを極限まで高めたいときや、内部計算用のワーク変数に値をロードするときに使う。

2. `DECIMAL(expression [, p [, q]])`

  • 表現を十進数(FIXED DECIMAL または FLOAT DECIMAL)に変換する。
  • 勘所:金額や数量など、1円の狂いも許されないビジネスロジックの結果を、VSAMや外部ファイルに出力する直前のフォーマット調整に使う。

3. `FLOAT(expression [, p])`

  • 表現を浮動小数点数に変換する。
  • 勘所:科学技術計算や、極端に大きな数値・小さな数値を扱う統計処理の際、桁あふれを防ぐために一時的に退避させる。

—

3. 実践!VSAMアクセスとONユニットを伴うPL/Iプログラム例

百聞は一見に如かず。実際の業務バッチを想定したサンプルコードを見せよう。
このコードでは、VSAM(KSDS)から読み込んだパック十進数の売上データを、`BINARY` 関数で安全に内部計算用に変換し、消費税を計算した上で、再び `DECIMAL` 関数で厳密な精度を指定して出力用レコードに組み立てている。さらに、万が一のデータ崩壊に備えて `SIZE` 条件を捕捉する `ON` ユニットも実装している。

1
/——————————————————————–不必-/
/ プログラム名: SLM0030I (売上集計・税額計算バッチ) /
/ 概要: VSAM(KSDS)から入力された生データを読み込み、明示的型変換を /
/ 用いて安全に計算を行い、結果を別ファイルに出力する。 /
/——————————————————————–不必-/
SLM0030I: PROC OPTIONS(MAIN);

DCL / VSAM入力レコード定義 (外部形式パック十進数) /
IN_REC,
3 IN_CUST_ID CHAR(5), / 顧客ID /
3 IN_AMT_DEC FIXED DEC(11,2) COMP3;/ 売上金額(11桁,小数2桁) /

DCL / 印刷・出力用レコード定義 /
OUT_REC,
$OUT_CUST_ID CHAR(5),
$OUT_TAX_DEC FIXED DEC(11,2) COMP3;/ 税額出力領域 /

DCL / 内部計算用ワーク変数 /
WK_AMT_BIN FIXED BIN(31) BUILTIN;/ 計算用バイナリ /
WK_TAX_BIN FIXED BIN(31) BUILTIN;

DCL / ファイル定義 /
VSAM_IN FILE RECORD SEQUENTIAL INPUT,
RPT_OUT FILE RECORD SEQUENTIAL OUTPUT;

DCL EOF_FLG CHAR(1) INIT(‘0’);

/ ——————————————————– /
/ ONユニット定義: サイズエラー(桁あふれ)のトラップ /
/ ——————————————————– /
ON SIZE BEGIN;
DISPLAY(‘ 致命的エラー: 演算結果の桁あふれを検知しました。’);
DISPLAY(‘ 該当顧客ID: ‘ || IN_CUST_ID);
SIGNAL ERROR; / 異常終了へ誘導 /
END;

/ ファイルオープン /
OPEN FILE(VSAM_IN) INPUT, FILE(RPT_OUT) OUTPUT;

/ 読み込みループ /
DO WHILE (EOF_FLG = ‘0’);
READ FILE(VSAM_IN) INTO(IN_REC);

IF 読み込み完了フラグ = ‘1’ THEN / ※説明用の擬似表現 /
EOF_FLG = ‘1’;
ELSE DO;
—————————————————-
— 【明示的変換の核心】
— 1. FIXED DEC (COMP3) のデータを BINARY に変換し、
— 高速かつ安全なレジスタ演算空間へ持ち込む。
—————————————————-
WK_AMT_BIN = BINARY(IN_AMT_DEC, 31, 0);

—————————————————-
— 2. バイナリ空間で消費税率(8%)を掛け合わせる
— ※ここでは簡略化のため整数演算として処理
—————————————————-
WK_TAX_BIN = WK_AMT_BIN 8 / 100;

—————————————————-
— 【明示的変換の核心】
— 3. 計算結果のBINARYを、出力用の FIXED DEC (11,2) に
— DECIMAL関数で明示的に型と精度を指定して押し戻す。
— これにより、暗黙の丸めによる予期せぬ切捨てを防ぐ。
—————————————————-
$OUT_CUST_ID = IN_CUST_ID;
$OUT_TAX_DEC = DECIMAL(WK_TAX_BIN, 11, 2);

/ 出力ファイルへ書き出し /
WRITE FILE(RPT_OUT) FROM(OUT_REC);
END;
END;

/ ファイルクローズ /
CLOSE FILE(VSAM_IN), FILE(RPT_OUT);

DISPLAY(‘ SLM0030I 正常終了 ‘);
RETURN;

END SLM0030I;

—

4. シニアアーキテクトからの実践アドバイスとデバッグの極意

このコードを見て、「なぜわざわざ `BINARY` に落としてから計算しているんだ? そのまま `FIXED DEC` で計算すればいいじゃないか」と思ったそこの君。甘い。

メインフレームのハードウェア(IBM Zのアーキテクチャ)において、十進数演算(Decimal Arithmetic)は専用の命令(SRPやZAPなど)を使うが、ループ回数が数十万、数百万回に及ぶ大規模バッチでは、バイナリ演算(Fixed Binary)の方がCPUサイクルの消費を抑えられるケースが多い。特に複雑な係数を掛け合わせるようなロジックでは、一度 `BINARY(…, 31, 0)` などでフルワードのレジスタサイズに展開して計算する方が、桁あふれのコントロールもしやすいのだ。

現場で陥りがちな罠

1. 精度の指定ミス($p$ と $q$ の不一致)
`DECIMAL(WK_TAX_BIN, 11, 2)` と指定したとき、元となるバイナリのスケールが破綻していると、コンパイル時に警告が出なくとも、実行時に高位桁が削ぎ落とされる。ビルトイン関数を使うときは、必ず「受け皿の変数の定義」と「関数の第2・第3引数の精度」を完全に一致させろ。
2. 浮動小数点 (`FLOAT`) との混同
金額計算に `FLOAT` 関数や `FLOAT` 型をうっかり使うな。浮動態は2進数ベースの近似値計算であるため、1円未満の端数処理で必ず「1ペニーのズレ(Rounding Error)」が発生し、経理部門との監査で血を見る羽目になる。金額・数量には必ず `DECIMAL` またはスケーリングされた `BINARY` を使うこと。

レガシーシステムの寿命を延ばすも縮めるも、我々エンジニアが書くコードの「厳密性」にかかっている。コンパイラの甘い誘惑(暗黙の型変換)を断ち切り、ビルトイン関数でデータを完全に手なずけること。これが、夜間バッチを時間通りに完走させるための唯一の王道だ。

さて、コーヒーを飲んだら、次のジョブストリームの結合テスト仕様書を確認するとしようか。頼むぞ、後輩くん。

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