こんにちは!IBMメインフレームの世界へようこそ。
JavaやCOBOLといったモダン、あるいはビジネスで定番の言語をバリバリ書いてきた方にとって、レガシーの代名詞とも言える「PL/I(ピーエルアイ)」は、最初ちょっと近寄りがたい雰囲気がしますよね。「なんだか記号だらけだし、変なルールがありそう…」なんて身構えてしまう方も多いのではないでしょうか。
でも、安心してください。ベースにある考え方を一つずつほぐしていけば、PL/Iは非常に合理的で、かつマシンパワーを極限まで引き出せる美しい言語です。
今回は、そんなPL/Iの懐の深さと、ちょっとユニークなデータ制御の裏側を覗いてみましょう。テーマは「TRUNC関数による整数部の切り出しと、その裏で行われているレジスタ操作」です。
Javaの `Math.floor()` や COBOLの `FUNCTION INTEGER()` とは一味違う、メインフレームならではの「ビットとハードウェアの息吹」を感じる世界へご案内します!
—
1. JavaやCOBOL出身者が驚く、PL/Iの「予約語がない」世界
まず、PL/Iを語る上で避けて通れない最大のカルチャーショック、それが「言語に予約語(Reserved Words)が存在しない」という事実です。
JavaやCOBOLでは、`IF` や `READ`、`DATA` といった単語は「言語の命令」として予約されているため、変数名に使うと怒られますよね。しかし、PL/Iにはそれがありません。
例えば、こんなコードが書けてしまいます。
1
DECLARE IF FIXED BIN (31);
IF = 10;
コンパイラは、前後の文脈(コンテキスト)から「これは変数名だな」「ここは制御文の `IF` だな」と空気を読んで判断します。めちゃくちゃ柔軟ですが、うっかり組み込み関数名やキーワードを変数名にしてしまうと、コードを読む人間(特に後任のプログラマ)が発狂しそうになるので、実務では絶対にお行儀の良い命名を心がけましょうね。
—
2. 浮動小数点数と整数のせめぎ合い:TRUNC関数の正体
さて、本題の「浮動小数点数から整数部を切り出す」お話です。
金額計算や科学技術計算でよく使われる浮動小数点数(PL/Iでは `FLOAT` 型)は、内部的には「符号」「指数部」「仮数部」という、まるで科学の教科書に出てくるような複雑な姿でメモリに鎮座しています。これを「そのままスパッと整数(`FIXED BIN` や `FIXED DEC`)として扱いたい!」という時に登場するのが、お馴染みの `TRUNC` 関数 です。
Java感覚で `TRUNC(X)` と書くと、単に「小数点以下が切り捨てられるんだな」と思いますよね。もちろん結果はそうなのですが、IBMメインフレームの心臓部(CPU)の挙動を覗くと、実はなかなかエキサイティングなことが起きています。
メモリの裏側で何が起きているのか?
浮動小数点レジスタ(FPR)に入っているデータは、そのままではCPUの演算器(汎用レジスタ / GPR)で整数としての足し算や添字の計算に使えません。
CPUに「おい、こいつの整数部だけを引っこ抜いて、汎用レジスタに放り込んでくれ!」と命令するためには、以下のステップを踏む必要があります。
1. 指数部のマスク処理と範囲チェック
浮動小数点数の「小数点がいまどこにあるか(指数部)」を読み取り、小数点を右や左にシフトするマスク演算を行います。
2. 整数レジスタ(GPR)への転送
綺麗に整数化されたビット列を、メインフレームの汎用レジスタ(R1からR15など)へとガチャンコとロードします。
この一連の流れを、PL/Iコンパイラは最適化されたマシン語(IBM Zのアーキテクチャ命令)へと翻訳してくれます。
—
3. 実践!PL/Iコードで見るTRUNCとデータ属性の妙
百聞は一見にしかず。実際に浮動小数点数から整数部を切り出し、レジスタ操作に近い感覚で処理するサンプルコードを見てみましょう。
1
/ ========================================================== /
/ 浮動小数点数から整数部を抽出し、整数レジスタ用変数に格納する例 /
/ ========================================================== /
TESTTRUNC: PROC OPTIONS(MAIN);
/ 変数の宣言 /
/ FLOAT DEC(6) は単精度浮動小数点数(IBMの 4バイト表現) /
DECLARE WK_FLOAT_VAL FLOAT DEC(6);
/ FIXED BIN(31) は32ビット符号付き整数。これは実質的に /
/ メインフレームの32ビット汎用レジスタ(GPR)にそのまま載るサイズです /
DECLARE WK_INT_RESULT FIXED BIN(31);
/ メッセージ出力用の文字型変数 /
DECLARE OUT_MSG CHAR(80);
/ 1. 浮動小数点数にデータを代入(例として 123.456 をセット) /
WK_FLOAT_VAL = 123.456;
/ 2. TRUNC関数で整数部のみを切り出す /
/ ここでコンパイラは、浮動小数点の内部表現を解釈し、 /
/ 整数データ型へ変換する最適な機械語コードを生成します。 /
WK_INT_RESULT = TRUNC(WK_FLOAT_VAL);
/ 3. 結果の確認 /
/ 組み込み関数 TRIM と PUT を使ってコンソールに出力します /
PUT SKIP EDIT (‘変換前の浮動小数点数: ‘, WK_FLOAT_VAL)
(A, F(10,3));
PUT SKIP EDIT (‘抽出された整数部(BIN31): ‘, WK_INT_RESULT)
(A, F(10));
END TESTTRUNC;
コードのポイント解説
- `FLOAT DEC(6)` と `FIXED BIN(31)` の組み合わせ
レガシー移行の現場で最も頭を悩ませるのが、このデータ属性のミスマッチです。`FLOAT` は誤差を生む可能性があるので、最終的にループのカウンタや配列の添字、あるいは外部DBのキー項目として使う場合は、必ずこのように `FIXED BIN(31)`(32ビット整数)へ綺麗にキャスト(あるいは `TRUNC` で変換)してやる必要があります。
- ハードウェアを意識したデータ型
`FIXED BIN(31)` は、IBMメインフレームのCPUが一番得意とする「一撃でレジスタにロードできるサイズ」です。ここを意識して設計されているコードは、実行性能(CPパフォーマンストラック)が非常に高くなります。
—
4. まとめ:レガシーの仕様は「怖くない」
いかがでしたでしょうか?
「TRUNC関数の整数部切り出し」という一見シンプルな処理の裏側でも、PL/Iという言語は、メインフレームのハードウェア仕様(浮動小数点レジスタと汎用レジスタの連携)と密接に結びついて動いています。
他の言語から来た方にとっては、最初は独特なデータ属性(`FLOAT` や `FIXED BIN`)や予約語の無い緩い構文に戸惑うかもしれません。しかし、「コンピュータがメモリとレジスタの間でどうデータをやり取りしているか」というハードウェアの基本に立ち返ってみると、PL/Iほど直感的でパワフルな言語はないことに気付くはずです。
もし現場で古いPL/Iのコードや、謎めいたデータ宣言に出くわしても、深呼吸して一つずつ属性を紐解いていけば大丈夫です。怖がる必要は全くありません。
あなたのメインフレーム開発ライフが、少しでも楽しく快適なものになりますように!
