ゼロ除算の罠:PL/IにおけるON ZERODIVIDEと、その深淵なる実務的解釈
メインフレームの現場において、「ゼロ除算(ZERODIVIDE)」ほど、その扱いがアーキテクトの腕を問うものはない。Javaのようなモダンな言語であれば`ArithmeticException`をキャッチして終了、というのが定石だが、PL/Iの世界では、その「例外」をどう料理するかで、基幹システムの信頼性が決定づけられる。
今回は、単なる構文解説に留まらず、コンパイラ最適化やマイグレーションの視点から、この古典的かつ極めて重要なエラーハンドリングの極意を紐解いていこう。
—
1. 基本構造とON ZERODIVIDEの責務
PL/Iプログラムにおいて、`ON ZERODIVIDE`は単なるエラー回避の道具ではない。それは、計算ロジックが破綻した際に、処理を「安全な状態」へ引き戻すための境界線だ。
まずは、最も基本的なハンドリング構造を見てほしい。
/i
/ メインプロシージャの開始 /
MAIN_PROC: PROCEDURE OPTIONS(MAIN);
/ ゼロ除算発生時のリカバリ処理ユニット /
ON ZERODIVIDE BEGIN;
PUT SKIP LIST(‘警告: ゼロ除算を検知。デフォルト値0で続行します。’);
/ ここに補正値を設定する /
CALC_RESULT = 0;
/ 処理を継続させるためのGOTO制御(多用は禁物だが、時には必要) /
GOTO CONTINUE_PROCESS;
END;
/ 実際の計算ロジック /
DCL (DIVIDEND, DIVISOR, CALC_RESULT) FIXED DEC(15,2);
/ 中略:DB2からのFETCHやファイル入力処理 /
CALC_RESULT = DIVIDEND / DIVISOR;
CONTINUE_PROCESS:
/ 以降のビジネスロジック /
…
END MAIN_PROC;
ここで重要なのは、`BEGIN; END;`ブロックによるスコープの管理だ。プログラムが複雑化し、`CALL`先で発生したゼロ除算をどうトラップするか。`ON`ユニットはスタック的に管理されるため、呼び出し先での意図しないハンドラ発動を防ぐための設計思想が求められる。
—
2. コンパイラ最適化と「見えないバグ」の正体
テックリードとして注意すべきは、`COMPILE`オプションの影響だ。最適化レベルが高い(`OPTIMIZE(3)`など)場合、コンパイラは「この除算は定数だからチェック不要」と判断し、ハードウェア命令レベルで最適化をかけることがある。
ここで厄介なのが、パックデシマル(FIXED DEC)の内部符号だ。
汎用機のレガシーなデータ形式では、符号ビットが正常な数値形式(X’C’やX’D’)以外の不正な値を持つ場合がある。ゼロ除算以前に、そもそも「数値ではない何か」を計算しようとして`S0C7`(データ例外)が飛ぶ。
- 教訓: `ON ZERODIVIDE`だけを信頼してはならない。必ず`ON CONDITION(ANYCONDITION)`や`ON ERROR`を併用し、予期せぬデータ破損にも備えるべきだ。
—
3. マイグレーションの最前線:Java/C#への移植
Javaへの移行時、最も現場が疲弊するのは「PL/IのONユニットの動作をどう再現するか」という問いだ。
PL/Iの`ON`ユニットは、実行時の動的なコンテキスト(スタックフレーム)を遡る仕組みだ。これをJavaの`try-catch`で再現しようとすると、スパゲッティコードが完成する。
移行設計においては、以下の戦略を推奨する。
- ビジネスロジックの分離: 計算ロジックを関数化し、戻り値として「成功/失敗」のステータスを返すインターフェースに作り替える。
- 例外の明示的なスロー: `ON`ユニットの暗黙的な制御に頼らず、検証ロジックを前置し、例外発生時にログを出す「ガード節」を実装する。
—
4. CICS/DB2環境におけるエッジケース
CICS環境下でのゼロ除算は、タスクそのものの異常終了(ABEND)を招き、最悪の場合、DB2のロックが解放されずに後続バッチを巻き込む。
- ダンプ解析のヒント: `SYSMDUMP`が出力された際、`PSW`(プログラムステータスワード)を確認し、どの命令でトラップされたかを確認せよ。もし`ON`ユニットを通っているはずなのに落ちているなら、それはハンドラ内での「二次的なゼロ除算」が原因であることが多い。
- DB2結合時の注意: DB2の`SELECT`文で除算を行う場合、PL/I側でハンドリングすることは不可能だ。`NULL`値やゼロによる除算がSQLレベルで発生する場合、`COALESCE`や`NULLIF`関数をSQL側に記述し、DB2の段階で潰しておくのが、メインフレームアーキテクトとしての「流儀」である。
—
まとめ:技術の先にあるもの
PL/Iのゼロ除算制御は、単なるプログラミングのテクニックではない。それは、ハードウェアの挙動を理解し、計算資源を如何にして停滞させずに運用し続けるかという、汎用機世代の執念そのものだ。
もしあなたが今、レガシーシステムの移行や改修を担っているのなら、コードの表面的な書き換えに終始してはならない。「なぜ、その場所でゼロが発生しうるのか?」という業務ロジックの深淵を読み解くことこそが、最も価値あるマイグレーションへの近道となるはずだ。
技術は変われど、エラーハンドリングの哲学は変わらない。堅牢なシステムを構築するためのヒントが、この記事の先にあることを信じている。
