问题
Apparently the CLR handles floating point exceptions by ignoring the exception and returning a default value. In the case of a floating point overflow for example, the CLR returns +Infinity, but no exception is raised. Delphi on the other hand, by default will raise an exception in the case of an overflow. This is controlled by FPU exception flags, and you can make Delphi do the same thing as the CLR by setting the exception flags accordingly. All well and good.
I have a numerical library which is written in Delphi, and which expects overflows to be raised as exceptions. It then handles these edge cases accordingly. If the exceptions are not raised, it doesn't know what to do. Now if I use this library in a COM dll and call that COM DLL from managed code, then overflows are not raised as exceptions and the library returns incorrect results. I have got over this by wrapping all calls to the library in the following code :
var
Mask : TFPUExceptionMask;
begin
Mask := Math.SetExceptionMask([exDenormalized, exUnderflow, exPrecision]);
try
//do my calls to the library
finally
Math.SetException(Mask);
end;
This seemed to work, but I have a case now where I get an overflow, and the CLR seems to take over control and Delphi never get's an EOverflow exception. It just doesn't get that far. It get's into a loop of overflow exceptions (floating point overflows rather than Delphi generated EOverflows) and crashes the application.
The only way of fixing this is to not use the mask above, find out where there are possible overflows and add this code some error handling code. Here's an example :
f1 := Power(X, Y);
if f1 = Infinity then
raise EOverflow.Create('Overflow');
This is not ideal, because I do not know the library well enough to guess all the possible areas I may get an overflow. But the above works fine.
Anybody had experience of this? Is it possible to tell the CLR to back off FPU exceptions and let Delphi handle them? Btw, if I call the same COM DLL from Excel VBA, there is the same mask problem, but using the first fix above, i.e. forcing the mask for the call, works fine. It's just managed code that has a problem.
So here's an example :
I have created a Delphi Active X library (DLL) with a single COM object. This object, has a single method called TestOverflow and is shown below :
function TTestExceptions.TestOverflow : Double;
var
Mask : TArithmeticExceptionMask;
begin
Mask := GetExceptionMask;
try
Result := Power(600, 30000);
except
On EOverflow do
Result := -1;
end;
end;
I then created three clients. One written in Delphi, One in EXCEL VBA and one in C#. Each client does exactly the same thing. Create an instance of the COM object, and call TestOverflow.
a) Delphi Client
Mask is [exDenormalized, exUnderflow, exPrecision]
An exception $C0000091 with message 'floating point overflow' is thrown and this is caught as an EOverflow exception and the function returns -1.
b) Excel Client
Mask is [exInvalidOp, exZeroDivide, exOverflow, exUnderflow, exPrecision]
No exception is thrown, and the function returns +INF as a result.
c) C# Client
Mask is [exInvalidOp, exZeroDivide, exOverflow, exUnderflow, exPrecision]
No exception is thrown, and the function returns +INF as a result.
I then change the function to manipulate the exception mask thus :
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;
This time I get :
a) Delphi Client
An exception $C0000091 with message 'floating point overflow' is thrown and this is caught as an EOverflow exception and the function returns -1.
b) Excel Client
An exception $C0000091 with message 'floating point overflow' is thrown and this is caught as an EOverflow exception and the function returns -1.
c) C# Client
An exception $C0000091 with message 'floating point overflow' is thrown but the execution doesn't continue, but stops at that line, and seems to get stuck at that line and just carries on throwing the same exception until it crashes.
来源:https://stackoverflow.com/questions/15024386/is-there-anyway-to-customise-how-net-handles-floating-point-exceptions