【入門編】PRECISION関数の引数と戻り値の仕様 – PL/Iの基本構文とデータ制御実践ガイド

こんにちは!メインフレームの世界へようこそ。
JavaやCOBOLといったモダン、あるいはビジネスチルドレンな言語をバリバリ書いてきた方にとって、IBMメインフレームの「PL/I(ピーエルアイ)」という名前を聞くだけで、なんだか古めかしい黒い画面と難しい呪文のようなコードを想像して身構えてしまうかもしれませんよね。

「なんだこの変数宣言は……?」
「四捨五入はどうやってやるんだ?」
「突然abend(異常終了)したんだけど!」

そんな風に不安を感じている方も、どうぞ安心してください。今日はPL/Iの数ある機能の中でも、特に数値計算のキモとなる`PRECISION`関数について、実務の現場でスッと腑に落ちるようにお話ししていきますね。

一緒に一つずつ、紐解いていきましょう。

—

1. 他言語とはちょっと違う? PL/Iの数値の世界と「PRECISION関数」

Javaなら `int` や `long`、COBOLなら `PIC 9(9) USAGE COMP` など、数値の入れ物はだいたい決まっていますよね。しかしPL/Iは、変数の「桁数(精度)」と「小数点位置(スケール)」を非常に細かく、まるでオーダーメイドのスーツのように自由自在に定義できる言語です。

例えば、こんな風に宣言します。

1
DCL WK_KINGAKU FIXED DEC(9, 2); / 全9桁、うち小数点以下2桁の固定小数点数 /

ここで問題になるのが、「計算の途中で桁数が変わっちゃった!」とか「別の精度の変数に値を代入したい!」という場面です。JavaやCOBOLならコンパイラが勝手によしなにやってくれたり、あるいは暗黙の切り捨てに気づかずバグを生んだりしますよね。

そこで登場するのが、今回の主役である`PRECISION`関数です。

PRECISION関数って何をするもの?

一言で言うと、「値の持つ精度(全体桁数と小数点以下の桁数)を、強制的に別の形にリサイズする関数」です。

書き方はこうです。
1
PRECISION ( 式, 全体桁数 [, スケール] )

「なんだ、ただの型キャストみたいなもんか」と思ったそこのあなた。実はここに、PL/I特有の「恐ろしいけど理にかなった」挙動が隠されているのです。

—

2. 切り捨て? 丸め? ―― 溢れた桁のゆくえ

実務で一番怖いのは、「計算結果が入りきらなくて、勝手に数字が削られちゃった!」というケースですよね。

`PRECISION`関数を使って、元の値よりも小さい桁数(あるいはスケール)に変換しようとしたとき、PL/Iは一体どう振る舞うのでしょうか?

実は「四捨五入」ではなく「切り捨て(Truncation)」です!

ここ、テストに出るくらい重要なポイントです。
COBOLの `ROUNDED` のような親切心は、PL/Iの基本機能としては発動しません。`PRECISION`関数で小数点以下を減らす場合、あふれた端数は問答無用で切り捨て(Truncate)されます。

イメージしてみましょう。
あなたは今、100円のケーキを3人で割る計算をしています。
計算結果が `33.3333…` 円になりました。これをシステム側で `FIXED DEC(5, 2)` という「小数点以下2桁」の箱に無理やり押し込もうと、`PRECISION(計算結果, 5, 2)` を使ったとします。

  • 元の値: `33.3333`
  • 変換後: `33.33` (後ろの `0.0033` はきれいさっぱり消去!)

「えっ、四捨五入してくれないの!?」と焦るかもしれませんが、PL/Iは基本的に「プログラマが意図した通りの桁数で正確に切り詰める」という硬派な思想で作られています。もし四捨五入したい場合は、あらかじめ `ROUND` 関数などを組み合わせて使う必要があるので注意してくださいね。

—

3. 恐怖のオーバーフロー! ON条件の発火タイミング

さて、ここからが本番です。
小さくする分には「切り捨て」で済む話ですが、「大きすぎる値を、小さな箱に無理やり入れようとしたとき」はどうなるでしょうか?

例えば、全体で5桁しか入らない箱(`FIXED DEC(5, 0)` など)に対して、10万という大きな値を `PRECISION` で押し込もうとした場合です。

この瞬間、PL/Iの内部では何が起きるでしょうか?
……そう、サイズエラー(オーバーフロー)です。

いきなりアベンド(強制終了)する?

Javaなら例外(Exception)が飛んでスタックトレースが出ますよね。COBOLなら処理継続するか異常終了するかは設定次第です。
PL/Iでは、このようなデータ例外が発生した際、何も対策をしていないとプログラムは即座にABEND(S0C7などのシステム異常終了)を起こしてガタガタと崩れ落ちます。

ですが、ここでPL/Iの真骨頂である「ON条件(ON-unit)」という強力なセーフティネットが登場します。

1
/ SIZE条件の捕捉ブロック /
ON SIZE BEGIN;
DISPLAY(‘【警告】数値のオーバーフローを検出しやした!安全な値に置き換えます。’);
/ ここで代替処理を書くことができます /
END;

`PRECISION` 関数を使った演算や代入によって、宣言された桁数に収まりきらないオーバーフローが発生した瞬間、PL/Iはこの `ON SIZE` 条件を即座に発火(キャッチ)させます。

この発火タイミングは、「実際に変数に値が格納され、あふれが検知されたそのコンマ数秒の瞬間」です。プログラムがクラッシュする前に、「おっと、入りきらないよ!」と自分でキャッチして、ログを出したり、デフォルト値を代入して処理を続行させたりすることが可能なのです。

レガシーシステムのバッチ処理において、「夜間バッチが途中で変なデータで落ちないようにハンドリングする」ための必須テクニックが、この組み合わせというわけですね。

—

4. 【実践】サンプルコードで動きを体感しよう!

百聞は一見にしかず。実際に動かすイメージができるよう、典型的なコード例を見てみましょう。実務のマイグレーション調査の際にも、こういう書き方をよく見かけるはずです。

1
—————————————————————-

  • プレシジョン関数とサイズエラーの挙動を確認するサンプルプログラム

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

DCL W_ORIGINAL FIXED DEC(7,2) VALUE(12345.67); / 元データ /
DCL W_CONVERTED FIXED DEC(4,1); / 小さい箱 /
DCL MSG_BUF CHAR(80);

/ — ① オーバーフローを検知するためのON条件定義 — /
ON SIZE BEGIN;
DISPLAY(‘— [TRAP] SIZE(オーバーフロー)条件を検知しました! —‘);
/ 無理やり入りきらなかった場合の救済措置 /
W_CONVERTED = 999.9;
END;

DISPLAY(‘=== 処理開始 ===’);

/ — ② 正常な範囲でのPRECISION変換(切り捨ての確認) — /
/ 全7桁・小数2桁のデータを、全体5桁・小数1桁に縮小する /
/ 12345.67 -> 整数部が足りなくなる可能性があるので注意が必要 /

/ ここでは分かりやすく、入りきるサイズでテスト /
W_CONVERTED = PRECISION(W_ORIGINAL, 5, 1);

/ 期待値:元の 12345.67 は 5桁(整数4,小数1)の箱には入らないため、SIZE条件が発火するはず! /

DISPLAY(‘変換後の値は: ‘ || W_CONVERTED);

DISPLAY(‘=== 処理終了 ===’);

END TEST_PREC;

このコードを実行すると、`W_ORIGINAL` の整数部分が大きすぎるため、`PRECISION` で指定した箱(全体5桁)に収まりきらず、見事に `ON SIZE` がキャッチされてメッセージが出力されます。

—

5. まとめ:PL/Iの数値制御は怖くない!

いかがでしたでしょうか?
「PL/Iのデータ型や関数はルールが厳しそう……」と感じていた方も、

1. `PRECISION` 関数は、桁数とスケールを強制的に再定義するもの。
2. はみ出た小数点以下は、四捨五入ではなく「切り捨て」られる。
3. 箱に入りきらないオーバーフローが起きても、`ON SIZE` を使えば優しく(あるいは力技で)キャッチして救出できる。

という基本の3つさえ押さえておけば、もう怖くありません。

他言語での経験があるあなたなら、PL/Iの緻密なデータ制御のロジックはすぐにマスターできるはずです。レガシーシステムの改修や移行プロジェクトでこの記述に出会ったときは、「あ、PL/Iがきちんと守りを固めてくれているんだな」と、優しく微笑みかけてあげてくださいね。

あなたのメインフレームライフが、エラーなしの快適なものになりますように!

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