Jump to content
FabDev

0/0 => EInvalidOp or NAN ?

Recommended Posts

Hello,

 

 

This :

procedure TForm2.Button3Click(Sender: TObject);
var
  V1, V2: Variant;
  I1, I2: Integer;
begin
  V1 := 0;
  V2 := 0;
  ShowMessage(V1 / V2);

  I1 := 0;
  I2 := 0;
  ShowMessage(FloatToStr(I1 / I2));
end;

 

Tested on 4 differents Delphi version (XE, 10.3, 10.4 or 11.3 pro or enterprise) both showmessage  raise an EInvalidOp exception.

Like described here :

https://docwiki.embarcadero.com/RADStudio/Sydney/en/Floating-Point_Exceptions

 

 

 

Tested on Windows 10 or 11 in VM or Physical. From a ten year old pentium to a two year old Core I7.

Same for Win32 or Win64 compiled debug or release. In debug mode or not. And same with or without GExpert, madexcept etc.
 

 

But for other Delphi user's using exactly the same project NaN is displayed !

 

 

 

Why ?

Does any Windows language settings can do it ?

 

 

 

TestDivisionByZero.zip

Edited by FabDev

Share this post


Link to post

var x: real;
begin
  SetExceptionMask(exAllArithmeticExceptions);
  x:=0/0  ;
  showmessage(FloatToStr(x));

 

Most probably some dll changes the exception mask.

Share this post


Link to post

Using project in attachment so there is no SetExceptionMask .

 

But what dll can do this ?

Share this post


Link to post

Check what the floating point exception mask is. I'm sure you'll find that invalid op is unmasked. Some code in your process is doing this. 

Share this post


Link to post

Problem all my Delphi (XE, 10.3, 10.4 and 11.3) do exception on VM and Physical Windows 10/11 :

 

 

The project in attachment :

procedure TForm23.Button1Click(Sender: TObject);
var
   v1,v2,v3:variant;
begin
     v1:=0;
     v2:=0;
     v3:=v1/v2;
end;

procedure TForm23.Button2Click(Sender: TObject);
var
   v1,v2:integer;

begin
     v1:=0;
     v2:=0;
     showmessage(Floattostr(v1/v2));
end;

procedure TForm23.Button3Click(Sender: TObject);
var
   v1,v2,v3:variant;
begin
     v1:=1;
     v2:=0;
     v3:=v1/v2;
end;


procedure TForm23.Button4Click(Sender: TObject);
var
   v1,v2:integer;

begin
     v1:=1;
     v2:=0;
     showmessage(Floattostr(v1/v2));
end;

 

Each 0/0 :

Raise exception 'Floating point invalid operation'  (EInvalidOP)

 

Each 1/0 :

Raise exception  'Floating point divide by 0'

 

And I have :

GetExceptionMask=[exInvalidOp,exDenormalized,exUnderflow]

 

 

Does somebody get different result ?

 

My problem it is that I can't reproduce the Nan result like some Delphi user's can have in the same project !!!

 

 

ExceptonDivisionBy0.zip

Edited by FabDev

Share this post


Link to post

Sorry to be so persistent, but is it really that exact same project that you posted, which reports differently at some of your clients machines? Or just your "real" project?

 

I've had the FPU mask getting changed behind my back by 3rd party dependencies like printer drivers or ActiveX plugins running in the TWebBrowser.

But in your example, there should be no third party code at all.

Edited by Der schöne Günther

Share this post


Link to post
22 minutes ago, Der schöne Günther said:

Sorry to be so persistent, but is it really that exact same project that you posted, which reports differently at some of your clients machines?

 

 

The project in my first post in this thread was coded by another Delphi user's (someone in the Support of a component library) and he said showmessage display Nan (= no exception) !!!

Edited by FabDev

Share this post


Link to post

But that is not a full project. Maybe the full project had something like a fancy graphic/charting library? Some other kind of 3rd party dependency? That is what I'm trying to say.

 

If you are working on a stand-alone project, you will need to find out what is changing this inside your application. If you are working on a library, you will either have to make sure that your code is always operating with the FPU mask it was designed for. Or, if that is not an option (FPU mask modification is a costly operation), then make it very clear in the documentation that the caller is responsible.

 

I have never heard that Windows sets the FPU mask differently for processes. However, I haven't been able to find much about it. I'd expect the Delphi RTL to set it at startup, but I haven't checked if it does.

Edited by Der schöne Günther

Share this post


Link to post

I had a similar problem with android, I solved it by trying to convert the result to string and then to number, so I modified your code a bit, but I can't test it with "NAN":

procedure TForm2.Button1Click(Sender: TObject);
var
  V1, V2, V3: Variant;
  I1, I2: Integer;
  S1    : String;
begin
  V1 := 0;
  V2 := 0;

  Try V3 := V1 / V2;
      Except
      V3 := -1;
  End;

  Try S1 := FloatToStr(V3);
      StrToFloat(S1);
      Except
      V3 := -1;
  End;

  If V3=-1
     Then ShowMessage('Division by zero')
     Else ShowMessage(V3);

  I1 := 0;
  I2 := 0;
  ShowMessage(FloatToStr(I1 / I2));
end;

 

Share this post


Link to post
27 minutes ago, Minox said:

I had a similar problem with android, I solved it by trying to convert the result to string and then to number, so I modified your code a bit, but I can't test it with "NAN":

Whatever problem this is attempting to solve, this isn't the solution

Share this post


Link to post

I'm honestly not sure why this topic is still open. It's the exception mask. Something is changing it. You don't have complete control over it. Other modules that are loaded into your process can change it. It's quite messy. It's been covered ad nauseum here, Borland forums, SO, etc.

 

Run this program to demonstrate how the different exception masks influence behaviour:

 

{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  System.Math;

procedure Main;
var
  x, y: Double;
  Mask: TArithmeticExceptionMask;
begin
  x := 0;
  y := 0;

  Mask := GetExceptionMask;

  SetExceptionMask(Mask + [exInvalidOp]);
  Writeln(x / y);

  SetExceptionMask(Mask - [exInvalidOp]);
  Writeln(x / y);
end;

begin
  try
    Main;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;
end.

 

For your scenario, when NaN is produced, clearly invalidop is masked. When the exception is raised it is unmasked.

 

Share this post


Link to post
41 minutes ago, David Heffernan said:

Whatever problem this is attempting to solve, this isn't the solution

 

Have you tested it? because it works for me, it won't be the best of programming, but if it works...

Share this post


Link to post
38 minutes ago, David Heffernan said:

I'm honestly not sure why this topic is still open. It's the exception mask. Something is changing it. You don't have complete control over it. Other modules that are loaded into your process can change it. It's quite messy. It's been covered ad nauseum here, Borland forums, SO, etc.

 

Run this program to demonstrate how the different exception masks influence behaviour:

 


{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  System.Math;

procedure Main;
var
  x, y: Double;
  Mask: TArithmeticExceptionMask;
begin
  x := 0;
  y := 0;

  Mask := GetExceptionMask;

  SetExceptionMask(Mask + [exInvalidOp]);
  Writeln(x / y);

  SetExceptionMask(Mask - [exInvalidOp]);
  Writeln(x / y);
end;

begin
  try
    Main;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;
end.

 

For your scenario, when NaN is produced, clearly invalidop is masked. When the exception is raised it is unmasked.

 

 

The problem is that if the division results in "NAN", no exception is raised, so at runtime the error cannot be detected.

Share this post


Link to post
1 hour ago, Minox said:

Have you tested it? because it works for me, it won't be the best of programming, but if it works...

The solution is to set the exception mask to unmask fp exceptions that you want converted into runtime exceptions.

 

That said, I don't know how exception masks work on non x86 platforms. 

 

Your code has the problem that if the division returns -1 then it will treat that as an error, but what do you think happens when you do -1 / 1? If you are going to hack it the way you are doing then at least use a boolean.

 

Although see below. If your platform doesn't handle exception masks as windows does then use IsNaN. 

Edited by David Heffernan

Share this post


Link to post
2 minutes ago, Minox said:

The problem is that if the division results in "NAN", no exception is raised, so at runtime the error cannot be detected.

Do you understand how fp exceptions masks work?

Share this post


Link to post
57 minutes ago, David Heffernan said:

Run this program to demonstrate how the different exception masks influence behaviour:

It doesn't work in Windows ARM. In both cases, the output is Nan.

Share this post


Link to post
43 minutes ago, Wagner Landgraf said:

It doesn't work in Windows ARM. In both cases, the output is Nan.

So for the original question that's not an issue because that is for Windows. 

 

For @Minox on Android then IsNaN is what is needed. 

Share this post


Link to post
1 hour ago, David Heffernan said:

So for the original question that's not an issue because that is for Windows. 

I didn't understand your statement. Isn't that the exact original question? This is what I see in original post:

 

"

But for other Delphi user's using exactly the same project NaN is displayed !

Why ?

Does any Windows language settings can do it ?

"

 

 

Share this post


Link to post
3 hours ago, Wagner Landgraf said:

I didn't understand your statement. Isn't that the exact original question? This is what I see in original post:

 

"

But for other Delphi user's using exactly the same project NaN is displayed !

Why ?

Does any Windows language settings can do it ?

"

 

 

 

The original question is for Windows. And the program I posted demonstrates behaviour with and without invalid op masked.

 

As stated repeatedly above the original question concerns behaviour that is determined by fp exception masks.

 

@Minox was talking about Android.

Edited by David Heffernan

Share this post


Link to post

 

Yes the original question was for win32 :

 

On 6/6/2023 at 6:02 PM, FabDev said:

Same for Win32 or Win64 compiled debug or release. In debug mode or not. And same with or without GExpert, madexcept etc.

 

 

 

 

And after reading this :

I need to have a "logical" answer in which case exception is not raised.

For the moment it's seems to be because of using of :

SetExceptionMask([exInvalidOp,  exZeroDivide]);

 

 

Edited by FabDev

Share this post


Link to post
22 hours ago, Wagner Landgraf said:

It doesn't work in Windows ARM. In both cases, the output is Nan.

Windows ARM <> Windows x86

Windows ARM in this case is much closer to Android.

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×