Is there anyway to customise how .NET handles floating point exceptions?

微笑、不失礼 提交于 2019-12-08 04:09:08

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!