こんにちは!メインフレームの世界へようこそ。
JavaやC、あるいはCOBOLといったモダン(あるいは別のレガシー)な言語をバリバリ書いてきた方にとって、IBMメインフレームの「PL/I(ピーエルアイ)」という言語は、最初にソースコードを見た瞬間、ちょっと面食らう存在かもしれませんね。
「なんだこの暗号のような大文字の羅列は……?」
「変数名に `IF` とか `TOTAL` とか書いてあるけど、これ怒られないの?」
大丈夫です。安心してください。最初は誰もが戸惑いますが、その裏にあるルールとコンパイラの「お節介な性格」を優しく紐解いていけば、PL/Iほど懐が深く、合理的な言語はありません。
今回は、そんなPL/Iの基本の中でも、特に「算術演算とデータの桁あふれ」に深く関わる、コンパイラオプションの隠れた実力者 `TRUNC` について、現場のリアルな視点からじっくり解説していきたいと思います。
—
1. まずは安心してください:PL/Iには「絶対的な予約語」がない
JavaやCOBOLを触ってきた方なら、「キーワード(予約語)のリストは絶対に覚えなきゃいけない」「変数名に `IF` や `READ` なんて使ったらエラーになる」という常識がおありでしょう。
しかし、PL/Iの最もユニークで、最初に出会うとひっくり返りそうになる仕様。それは、
「PL/Iには、文脈依存のキーワードはあるけれど、厳密な意味での『予約語(Reserved Words)』が存在しない」
ということです。
どういうことか? 極端な話、以下のようなコードもPL/Iではエラーになりません。
1
/ 衝撃の変数名宣言の例 /
DECLARE IF FIXED BINARY(31);
DECLARE THEN FLOAT;
DECLARE ELSE CHARACTER(10);
IF = 10;
THEN = 3.14;
ELSE = ‘HELLO’;
「えっ、`IF`文なのに `IF` が変数!?」ですよね。
コンパイラは、コードの前後関係(文脈)を見て、「あ、ここは構文としての `IF` じゃなくて、ユーザーが定義した変数だな」と賢く判断してくれます。
もちろん、実際の開発現場で `IF` なんて名前の変数をわざわざ作る人はいませんが(後輩から冷ややかな目で見られますからね)、この「柔軟すぎる構文規則」のおかげで、言語仕様のバージョンアップや他システムからの移行時にも、キーワード衝突でソースコードが真っ青になるリスクが極めて低いのです。PL/Iは、見た目はいかついですが、実はとっても懐が広い言語なんですよ。
—
2. 本日の主役:バイナリデータの「お行儀」を決める `TRUNC` オプション
さて、ここからが本題です。
PL/Iで数値を扱う際、私たちは `FIXED BINARY`(2進固定小数点数、いわゆるバイナリ整数)をよく使います。COBOLでいうところの `COMP` や `COMP-4`、C言語の `int` や `long` に近いものです。
この `FIXED BINARY` を使うとき、メインフレームのコンパイラ(Enterprise PL/Iなど)に指示するオプションの一つが `TRUNC`(Truncationの略) です。
これ、バッチの移行や性能チューニングの現場で、知らずにハマると夜中に冷や汗をかく原因ナンバーワンと言っても過言ではない、非常に重要な設定なんです。
TRUNCには何があるの?
主に以下の2つのモードがあります。
1. `TRUNC(BIN)` (バイナリ基準)
- 宣言された桁数(例: `FIXED BIN(15)` なら2バイト、`FIXED BIN(31)` なら4バイト)の最大値まで、レジスタのビットをフルに使います。
- 例:`FIXED BIN(15)` であれば、変数は最大 32,767 まで格納できますが、実際のハードウェア(レジスタ)の大きさである 16ビット(65,535)まで値が入ることがあります。
2. `TRUNC(STD)` (スタンダード=COBOL/ANSI標準基準)
- 宣言された桁数(ピクチャや精度)を超えた演算結果を、厳格に切り捨て(Truncate)ます。
- 例:`FIXED BIN(15)`(精度15桁、つまり十進数で3万2767まで)と宣言していれば、演算結果がたとえ32,800になっても、強制的に指定された桁数に丸められます。
—
3. 生成される機械語コードへの影響と、パフォーマンスの罠
「で、それが私たちのシステムにどう影響するの?」という話ですよね。
システムアーキテクトの視点から言わせてもらうと、この `TRUNC` の設定の違いは、CPUの実行速度(パフォーマンス)に直結します。
TRUNC(BIN) の場合:スピードスター
ハードウェア(IBM Zのプロセッサ)は、基本的人権ならぬ「基本単位」として32ビット(フルワード)や64ビットのレジスタで計算を行います。
`TRUNC(BIN)` が指定されている場合、コンパイラは余計な「切り捨て・丸め処理の機械語命令(マスク処理など)」を生成しません。ハードウェアが計算した生の数値をそのまま高速に変数に突っ込みます。
- メリット: 処理が速い!余計なCPU命令を削れるため、大量件数を処理する基幹バッチに向いています。
- デメリット: 宣言した桁数を超えるゴミデータ(オーバーフロー値)がこっそり入り込む可能性があり、予期せぬバグの温床になることがあります。
TRUNC(STD) の場合:お堅いお巡りさん
COBOLや他の言語から移行してきた場合、データの整合性を厳格に保つために `TRUNC(STD)` を選ぶケースがよくあります。
コンパイラは、算術演算が行われるたびに、「おい、この変数の宣言桁数を超えてないか?」を確認し、超えていれば指定された範囲に丸めるための追加の機械語命令(マスク処理や変換命令)を必ず生成します。
- メリット: COBOL等の他言語との親和性が高く、仕様通りの厳格なデータ範囲が保証される。
- デメリット: CPU命令が増える。 1回あたりの差はマイクロ秒の世界ですが、1億件ループする夜間バッチでこれが積み重と、確実にバッチウィンドウ(処理時間枠)を圧迫します。
—
4. 実務で役立つ!PL/Iコード例とコンパイラ指令
実際のPL/Iソースコードでは、このコンパイラオプションをソース内のプリプロセッサ文(`PROCESS` ステートメント)で指定することができます。
実際の現場でよく見かける、パフォーマンスを意識したコードの書き方を見てみましょう。
1
PROCESS SYSTEM,
OPT(2),
TRUNC(BIN); / ここでTRUNC(BIN)を指定し、無駄な丸め処理を排除する /
DEMO_TRUNC: PROC OPTIONS(MAIN);
/ ————————————————– /
/ 変数宣言 /
/ ————————————————– /
/ FIXED BIN(31) は 4バイトの整数(C言語のlong相当) /
DCL LOOP_COUNTER FIXED BIN(31) INIT(0);
DCL TOTAL_AMT FIXED BIN(31) INIT(0);
DCL I FIXED BIN(15); / 小さな整数(2バイト) /
/ ————————————————– /
/ 処理ロジック /
/ ————————————————– /
PUT SKIP LIST(‘ 処理開始: TRUNC(BIN)による高速演算 ‘);
/ 大量データを模したループ処理 /
DO LOOP_COUNTER = 1 TO 1000000;
/ 加算処理 /
TOTAL_AMT = TOTAL_AMT + 10;
END;
PUT SKIP LIST(‘累計金額: ‘, TOTAL_AMT);
PUT SKIP LIST(‘ 処理終了 ‘);
END DEMO_TRUNC;
💡 アーキテクトからのワンポイントアドバイス
もし、貴方が今進めているマイグレーションプロジェクトで「COBOL資産をそのままPL/Iに置き換えたのに、なぜか一部の数値計算で結果が微妙に違うぞ?」という問題に直面したなら、それは大抵この `TRUNC` のデフォルト挙動(コンパイラ・インストールの初期値)の違いが原因です。
IBM製コンパイラのデフォルトは伝統的に `TRUNC(STD)` ですが、高パフォーマンスを要求される超大規模金融バッチなどでは、あえて `TRUNC(BIN)` に切り替えてチューニングを行っているケースが多いです。移行設計の際は、前システムのデータ定義とコンパイラオプションの組み合わせを必ず突き合わせるようにしてくださいね。
—
おわりに
いかがでしたでしょうか?
「予約語がない」という自由度の高さと、「TRUNCオプション」に見るハードウェア直結のシビアな最適化。PL/Iは、一見すると古めかしく見えますが、実は非常に合理的に作られたプロフェッショナルのための言語です。
「レガシーシステムだから……」と身構える必要は全くありません。一つひとつの仕様を紐解いていけば、そこにはエンジニアリングのロジックがしっかりと詰まっています。
あなたのメインフレーム開発・移行の旅が、少しでも快適で楽しいものになりますように。それではまた、次回の技術解説でお会いしましょう!
