こんにちは!メインフレームの世界へようこそ。
JavaやCOBOLといった他のモダンな言語をご経験されてきた方にとって、IBMメインフレームの世界、そして「PL/I(ピーエルアイ)」という言語は、どこか要塞のような、近寄りがたい雰囲気を感じさせてしまうかもしれませんよね。
「なんだか古い仕様が多そう…」「変数の宣言や型が独特で怖そう…」
そんな不安を抱えていらっしゃるかもしれませんが、どうか安心してください。一つひとつのルールの背景にある「意図」を紐解いていけば、PL/Iほど合理的で、かつプログラマの意図に忠実に動いてくれる言語は他にありません。
今回は、そんなPL/Iの懐の深さと、浮動小数点を取り扱う際のちょっぴり奥深い世界について、一緒に覗いていきましょう。テーマはずばり、「CEIL関数とFLOOR関数の浮動小数点制御」です!
—
予約語を持たない!?PL/Iの自由な世界と変数命名
まず、他の言語(JavaやCOBOLなど)からやってきたエンジニアが一番最初に驚くのが、「PL/Iには明確な予約語(Reserved Words)という概念がほぼ存在しない」という事実です。
Javaなら `int` や `class`、COBOLなら `MOVE` や `COMPUTE` などの単語を勝手な変数名に使うとコンパイルエラーになりますよね。でも、PL/Iの世界では、数学の関数名や命令語すらも、あなたが「変数名」として自由に使えてしまうのです。
例えば、こんなコードが書けてしまいます。
1
/ 衝撃の変数宣言:CEILという名前の変数を作っちゃう例 /
DCL CEIL FIXED DEC(5,0);
「えっ、それじゃあシステムは関数なのか変数なのか混乱しないの?」と思われるかもしれませんが、そこは長年進化し続けてきたメインフレームのコンパイラ。前後の文脈(コンテキスト)から、「あ、ここは関数を呼び出そうとしているな」「ここは変数を参照しているな」と、驚くほど賢く判断してくれます。
とはいえ、実務の現場では、コンパイルエラーにならないからといって `CEIL` を変数名にするのは、後からコードを読む同僚(あるいは未来のあなた!)が泣いてしまうので絶対にやめましょうね(笑)。
—
浮動小数点の丸め、その裏側で何が起きているのか?
さて、本題の `CEIL`(切り上げ)関数と `FLOOR`(切り捨て)関数についてです。
金利計算や税額計算、あるいはバッチ処理での端数処理など、実務でこの2つの関数にお世話にならない日はありません。
JavaやC言語を使っている方なら、これらは「単に小数点を丸める関数」としてお馴染みだと思います。しかし、IBMメインフレームのハードウェア(浮動小数点レジスタ)の仕組みと絡むと、PL/Iならではの厳密なデータ属性が顔を出します。
浮動小数点って、実は「誤差」とのお友達
コンピュータの世界では、小数(例: `0.1`)を2進数で正確に表現できません。そのため、内部的には「概算値」として保持されています。
例えば、内部的に `3.0000000000000004` のような値になっている浮動小数点数を `CEIL` に渡したらどうなるでしょうか?
人間なら「3.0なんだから切り上げても3でしょ」と思いたいところですが、コンピュータは厳密に「3より少し大きい値」と判断し、容赦なく `4` に切り上げてしまいます。この「予期せぬ1円のズレ」や「件数の狂い」が、夜間バッチの金額不一致を引き起こす典型的な原因なんですね。
実務で使える!PL/Iでの堅牢な丸め制御コード
レガシーシステムの移行や新規開発において、浮動小数点の思わぬワナにハマらないための実用的なPL/Iコード例を見てみましょう。
1
————————————————————–;
- 浮動小数点のCEIL/FLOOR制御サンプルプログラム
————————————————————–;
TEST_FLOOR: PROC OPTIONS(MAIN);
/ 変数の宣言 /
/ 浮動小数点(FLOAT DEC(16))はハードウェアのレジスタを使用 /
DCL W_FLOAT_VAL FLOAT DEC(16);
/ 演算結果を受け取る固定小数点(FIXEDDECIMAL) /
DCL W_CEIL_RESULT FIXED DEC(10,2);
DCL W_FLOOR_RESULT FIXED DEC(10,2);
/ 1. テストデータを代入します /
/ 微小な誤差を含みやすい値をあえて設定してみましょう /
W_FLOAT_VAL = 123.001;
/ 2. CEIL関数:引数以上の最小の整数(または指定桁)を返す /
/ 123.001 より大きい最小の整数は「124」になります /
W_CEIL_RESULT = CEIL(W_FLOAT_VAL);
/ 3. FLOOR関数:引数以下の最大の整数を返す /
/ 123.001 以下で最大の整数は「123」になります /
W_FLOOR_RESULT = FLOOR(W_FLOAT_VAL);
/ 結果の出力(実際にはSYSPRINTやファイルに出力します) /
PUT SKIP EDIT (‘元データ : ‘, W_FLOAT_VAL) (A, F(10,3));
PUT SKIP EDIT (‘CEIL結果 : ‘, W_CEIL_RESULT) (A, F(10,2));
PUT SKIP EDIT (‘FLOOR結果 : ‘, W_FLOOR_RESULT) (A, F(10,2));
END TEST_FLOOR;
このコードのポイントは、計算の土台となるデータを `FLOAT`(浮動小数点)で持ちつつ、最終的な保持や出力の器を `FIXED DEC`(固定小数点・ゾーン十進数やパック十進数)に明確に切り分けている点です。
PL/Iコンパイラは、`FLOAT` から `FIXED` へ値を代入する際、内部的に自動的な型変換と丸めを行ってくれます。しかし、どのタイミングでどの関数(`CEIL` / `FLOOR`)を噛ませるかによって、計算結果の端数が切り上げられるか切り捨てられるかが完全に決まります。
—
メインフレーム開発を怖がらなくて大丈夫です
ここまで、PL/Iの柔軟な命名規則や、浮動小数点を取り扱う際の `CEIL` / `FLOOR` の挙動についてお話ししてきました。
「予約語がない」という自由度の高さも、「ハードウェアのレジスタ特性に起因する浮動小数点の誤差」も、知ってしまえば怖くありません。むしろ、IBMメインフレームという強力なハードウェアの力を極限まで引き出し、ミリ秒単位の処理を最適化するための「先人の知恵」が詰まった美しい仕様です。
他の言語でのご経験があるあなたなら、PL/Iの基本文法なんてすぐにマスターできます。
日々のバッチ改修やマイグレーションの調査で分からない壁にぶぶつかったときは、「一つひとつ、データの属性と器(DCL)を確認していけば絶対に大丈夫」と心に留めておいてくださいね。
あなたのメインフレーム開発ライフが、実り多い素晴らしいものになりますように!
