CLR は、例外を無視してデフォルト値を返すことで、浮動小数点例外を処理しているようです。たとえば、浮動小数点オーバーフローの場合、CLR は +Infinity を返しますが、例外は発生しません。一方、Delphi では、デフォルトで、オーバーフローの場合に例外が発生します。これは FPU 例外フラグによって制御され、それに応じて例外フラグを設定することで、Delphi に CLR と同じことをさせることができます。すべて順調です。
Delphi で記述された数値ライブラリがあり、オーバーフローが例外として発生することを期待しています。次に、これらのエッジ ケースを適切に処理します。例外が発生しない場合、何をすべきかわかりません。このライブラリを COM dll で使用し、マネージ コードからその COM DLL を呼び出すと、オーバーフローは例外として発生せず、ライブラリは正しくない結果を返します。ライブラリへのすべての呼び出しを次のコードでラップすることで、これを乗り越えました。
var
Mask : TFPUExceptionMask;
begin
Mask := Math.SetExceptionMask([exDenormalized, exUnderflow, exPrecision]);
try
//do my calls to the library
finally
Math.SetException(Mask);
end;
これは機能しているように見えましたが、オーバーフローが発生し、CLR が制御を引き継ぐように見え、Delphi が EOverflow 例外を取得しないケースがあります。そこまでは行きません。オーバーフロー例外のループ (Delphi が生成した EOverflow ではなく浮動小数点オーバーフロー) に入り、アプリケーションをクラッシュさせます。
これを修正する唯一の方法は、上記のマスクを使用せず、オーバーフローの可能性がある場所を見つけて、このコードにエラー処理コードを追加することです。例を次に示します。
f1 := Power(X, Y);
if f1 = Infinity then
raise EOverflow.Create('Overflow');
これは理想的ではありません。オーバーフローが発生する可能性のあるすべての領域を推測できるほどライブラリをよく知らないためです。しかし、上記はうまくいきます。
誰もこれを経験しましたか?CLR に FPU 例外をバックオフして Delphi に処理させるように指示することは可能ですか? ところで、Excel VBA から同じ COM DLL を呼び出すと、同じマスクの問題が発生しますが、上記の最初の修正、つまり呼び出しにマスクを強制する方法を使用すると、問題なく動作します。問題があるのはマネージ コードだけです。
だからここに例があります:
単一の COM オブジェクトを使用して Delphi Active X ライブラリ (DLL) を作成しました。このオブジェクトには、TestOverflow と呼ばれる単一のメソッドがあり、以下に示されています。
function TTestExceptions.TestOverflow : Double;
var
Mask : TArithmeticExceptionMask;
begin
Mask := GetExceptionMask;
try
Result := Power(600, 30000);
except
On EOverflow do
Result := -1;
end;
end;
次に、3 つのクライアントを作成しました。1 つは Delphi で記述され、1 つは EXCEL VBA で、もう 1 つは C# で記述されています。各クライアントはまったく同じことを行います。COM オブジェクトのインスタンスを作成し、TestOverflow を呼び出します。
a) Delphi クライアント
マスクは [exDenormalized、exUnderflow、exPrecision]
メッセージ「浮動小数点オーバーフロー」を含む例外 $C0000091 がスローされ、これは EOverflow 例外としてキャッチされ、関数は -1 を返します。
b) Excel クライアント
マスクは [exInvalidOp, exZeroDivide, exOverflow, exUnderflow, exPrecision]
例外はスローされず、関数は結果として +INF を返します。
c) C# クライアント
マスクは [exInvalidOp, exZeroDivide, exOverflow, exUnderflow, exPrecision]
例外はスローされず、関数は結果として +INF を返します。
次に、例外マスクを操作する関数を次のように変更します。
function TTestExceptions.TestOverflow : Double;
var
Mask : TArithmeticExceptionMask;
begin
Mask := Math.SetExceptionMask([exDenormalized, exUnderflow, exPrecision]);
try
try
Result := Power(600, 30000);
except
On EOverflow do
Result := -1;
end;
finally
Math.SetExceptionMask(Mask);
end;
end;
今回は次のようになります。
a) Delphi クライアント
メッセージ「浮動小数点オーバーフロー」を含む例外 $C0000091 がスローされ、これは EOverflow 例外としてキャッチされ、関数は -1 を返します。
b) Excel クライアント
メッセージ「浮動小数点オーバーフロー」を含む例外 $C0000091 がスローされ、これは EOverflow 例外としてキャッチされ、関数は -1 を返します。
c) C# クライアント
メッセージ「浮動小数点オーバーフロー」を含む例外 $C0000091 がスローされますが、実行は続行されず、その行で停止し、その行でスタックしたように見え、クラッシュするまで同じ例外をスローし続けます。