Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2009-10

Division by Zero: EDivByZero and EZeroDivide

20 messages · 8 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Bart

Hi,

Is there a reason why in fpc both floating point and integer division by zero raise an EDivByZero exception?

See: http://docwiki.embarcadero.com/VCL/en/SysUtils.EZeroDivide

SysUtils.EZeroDivide exception is raised when an application attempts to divide a floating-point value by zero.
Note: Integer divide-by-zero errors raise the SysUtils.EDivByZero exception.

program zerodiv;
{$IFDEF FPC}
{$mode objfpc}{$H+}
{$ENDIF}
uses Sysutils;
var r,x: extended;
    i,j: integer;
begin
  i := 0;
  r := 0.0;
  try
    write('Integer division by zero: 1 div 0   -> ');
    j := 1 div i;
    writeln(j);
  except
    on e: exception do writeln(e.classname);
  end;
  try
    write('Float division by zero  : 1.0 / 0.0 -> ');
    x := 1.0 / r;
    writeln(x);
  except
    on e: exception do writeln(e.classname);
  end;
end.

Output with Delphi (3.0:
Integer division by zero: 1 div 0 -> EDivByZero
Float division by zero : 1.0 / 0.0 -> EZeroDivide

Output with Fpc 2.2.4 (tested on win32 and Linux i386) Integer division by zero: 1 div 0 -> EDivByZero
Float division by zero : 1.0 / 0.0 -> EDivByZero

Is this by design, or should I report it as a bug? (B.t.w. setting mode to delphi makes no difference)

Bart

Reply by email

Tom Verhoeff
to Bart
Bart wrote 9 lines
Is there a reason why in fpc both floating point and integer division by zero raise an EDivByZero exception? See: http://docwiki.embarcadero.com/VCL/en/SysUtils.EZeroDivide SysUtils.EZeroDivide exception is raised when an application attempts to divide a floating-point value by zero. Note: Integer divide-by-zero errors raise the SysUtils.EDivByZero exception.

It would be nicer if one had the ability to make floating-point division by zero return an IEEE 754 plus/minus infinity, without raising an exception. This is e.g. useful when one needs to evaluate rational functions. Without such an infinity, you need to make a nasty case analysis, which furthermore depends on the rational function.

A simple example is the situation where one needs to calculate the replacement resistor value R for parallel resistors having values R1, R2, ..., Rk. The formula is R = 1/(1/R1 + 1/R2 + ... + 1/Rk). The formula gives a divide-by-zero if one of the resistors has value 0. But in that case, the replacement value R also equals 0. When allowing infinities, it just works out fine (infinity + x = infinity, 1/infty = 0). That is precisely why IEEE 754 has infinities. Also see

<http://www.cs.berkeley.edu/~wkahan/ieee754status/why-ieee.pdf>

for other examples and further motivation.

Unfortunately, many compilers still do not fully support the IEEE 754 floating-point standard.

Best regards,

Tom

E-MAIL: T.Verhoeff @ TUE.NL | Dept. of Math. & Comp. Science PHONE: +31 40 247 41 25 | Technische Universiteit Eindhoven FAX: +31 40 247 54 04 | PO Box 513, NL-5600 MB Eindhoven http://www.win.tue.nl/~wstomv/ | The Netherlands

Reply by email

Jorge Aldo G. de F. Junior
to Tom Verhoeff

Wouldnt a NaN (Not a number) be more "matematically correct" result (I saw that on an old book about i387)

Matematically division by zero is an "mathematical impossibility", so NaN would be more logical

Is there a way to deal with NaN's and other i387 (and similars) conventions directly in Pascal ?

Something like :

If A / B = NaN then something();

2009/10/18 Tom Verhoeff

Show quoted text 47 lines
On Sat, Oct 17, 2009 at 01:57:28PM +0200, Bart wrote: > > Is there a reason why in fpc both floating point and integer division > by zero raise an EDivByZero exception? > > See: http://docwiki.embarcadero.com/VCL/en/SysUtils.EZeroDivide > > SysUtils.EZeroDivide exception is raised when an application attempts > to divide a floating-point value by zero. > Note: Integer divide-by-zero errors raise the SysUtils.EDivByZero exception. It would be nicer if one had the ability to make floating-point division by zero return an IEEE 754 plus/minus infinity, without raising an exception. This is e.g. useful when one needs to evaluate rational functions. Without such an infinity, you need to make a nasty case analysis, which furthermore depends on the rational function. A simple example is the situation where one needs to calculate the replacement resistor value R for parallel resistors having values R1, R2, ..., Rk. The formula is R = 1/(1/R1 + 1/R2 + ... + 1/Rk). The formula gives a divide-by-zero if one of the resistors has value 0. But in that case, the replacement value R also equals 0. When allowing infinities, it just works out fine (infinity + x = infinity, 1/infty = 0). That is precisely why IEEE 754 has infinities. Also see <http://www.cs.berkeley.edu/~wkahan/ieee754status/why-ieee.pdf<http://www.cs.berkeley.edu/%7Ewkahan/ieee754status/why-ieee.pdf> > for other examples and further motivation. Unfortunately, many compilers still do not fully support the IEEE 754 floating-point standard. Best regards, Tom -- E-MAIL: T.Verhoeff @ TUE.NL | Dept. of Math. & Comp. Science PHONE: +31 40 247 41 25 | Technische Universiteit Eindhoven FAX: +31 40 247 54 04 | PO Box 513, NL-5600 MB Eindhoven http://www.win.tue.nl/~wstomv/ <http://www.win.tue.nl/%7Ewstomv/> | The Netherlands _______________________________________________ fpc-pascal maillist - http://lists.freepascal.org/mailman/listinfo/fpc-pascal

Reply by email

Aleksa Todorovic
to Jorge Aldo G. de F. Junior: Wouldnt a NaN (Not a number) be more "matematically correct" result (I saw that on an old…
Jorge Aldo G. de F. Junior wrote 8 lines
Wouldnt a NaN (Not a number) be more "matematically correct" result (I saw that on an old book about i387) Matematically division by zero is an "mathematical impossibility", so NaN would be more logical Is there a way to deal with NaN's and other i387 (and similars) conventions directly in Pascal ?

Take a look at SysUtils, functions IsNaN, IsInfinite.

Show quoted text 14 lines
2009/10/18 Tom Verhoeff > > On Sat, Oct 17, 2009 at 01:57:28PM +0200, Bart wrote: > > > > Is there a reason why in fpc both floating point and integer division > > by zero raise an EDivByZero exception? > > > > See: http://docwiki.embarcadero.com/VCL/en/SysUtils.EZeroDivide > > > > SysUtils.EZeroDivide exception is raised when an application attempts > > to divide a floating-point value by zero. > > Note:  Integer divide-by-zero errors raise the SysUtils.EDivByZero > > exception.

Maybe because is more consistent to have one type for both exceptions
- when you see it, you know what it means. How could anybody make distinction between EDivByZero and EZeroDivide without looking at documentation?

Show quoted text 7 lines
> > It would be nicer if one had the ability to make floating-point division > by zero return an IEEE 754 plus/minus infinity, without raising an > exception.  This is e.g. useful when one needs to evaluate rational > functions.  Without such an infinity, you need to make a nasty case > analysis, which furthermore depends on the rational function. >

There is function SetExceptionMask in SysUtils which you can use to control which exceptions will be haven't used it, but looks like a good place to start...

Reply by email

Bart
to Jonas Maebe

Hi all,

Interesting thoughts.

However, if we raise an exception, why do we raise EDivByZero instead of EZeroDivide?

Note: EDivByZero = class(EIntError), so IMHO it makes no sense to raise it with a floating point error.

Bart

Reply by email

Tom Verhoeff
to Jorge Aldo G. de F. Junior: Wouldnt a NaN (Not a number) be more "matematically correct" result (I saw that on an old…
Jorge Aldo G. de F. Junior wrote
Wouldnt a NaN (Not a number) be more "matematically correct" result (I saw that on an old book about i387)

No, read Kahan's article that I pointed to.

Matematically division by zero is an "mathematical impossibility", so NaN would be more logical

There are situations (that occur e.g. in numerical approximations of mathematical models in physics) where division by zero is a well-defined infinity. For that reason, the IEEE 754 floating-point standard has distinct plus zero, minus zero, plus infinity, and minus infinity. There are precise (and useful) rules to calculate with these special numbers. They are not NaNs.

Many processors fully support the IEEE 754 standard, but many compilers don't. Partial support of the standard in the compiler makes the standard much less useful.

In our department, especially the numericists disliked (the old) Pascal because of the fixed array sizes and the lack of good support for floating-point arithmetic (i.e. IEEE 754 since 1985). I guess few (Free)Pascal users nowadays come from the numerical/scientific computing community.

Another (longish) "must read" about floating-point:

David Goldberg.
"What Every Computer Scientist Should Know about Floating-Point Arithmetic". <http://www.validlab.com/goldberg/paper.ps>

Note that IEEE Std 754 was revised in 2008, see e.g.

<http://en.wikipedia.org/wiki/IEEE_754-2008>

Best regards,

Tom

E-MAIL: T.Verhoeff @ TUE.NL | Dept. of Math. & Comp. Science PHONE: +31 40 247 41 25 | Technische Universiteit Eindhoven FAX: +31 40 247 54 04 | PO Box 513, NL-5600 MB Eindhoven http://www.win.tue.nl/~wstomv/ | The Netherlands

Reply by email

Tom Verhoeff
to Jonas Maebe: http://www.freepascal.org/docs-html/rtl/math/setexceptionmask.html Fully cross-platform,…
Jonas Maebe wrote 11 lines
On 18 Oct 2009, at 17:52, Tom Verhoeff wrote: > It would be nicer if one had the ability to make floating-point > division > by zero return an IEEE 754 plus/minus infinity, without raising an > exception. http://www.freepascal.org/docs-html/rtl/math/setexceptionmask.html Fully cross-platform, even.

How new is that?

It works 'partly'. Consider the program

program DivideByZero;
  { to see whether division by zero can result in Infinite values }
uses Math;
var one, zero, result: Double;
begin
  SetExceptionMask ( [ exZeroDivide{, exPrecision} ] );
  one := 1.0;
  zero := 0.0;
  result := one / zero;
  writeln ( result ); { Expected output: +Inf }
  result := one / result;
  writeln ( result ); { Expected output: +0.0 }
end.

Outputs:
+Inf
An unhandled exception occurred at $000060B0 :
EDivByZero : Division by zero
$000060B0
$00005850
$0001889B

In order to allow 1 / +Inf, you also need to mask exPrecision (i.e. uncomment it from the set passed to SetExceptionMask). I am surprised that division by an Infinity raises EDivByZero: Division by zero

Best regards,

Tom

E-MAIL: T.Verhoeff @ TUE.NL | Dept. of Math. & Comp. Science PHONE: +31 40 247 41 25 | Technische Universiteit Eindhoven FAX: +31 40 247 54 04 | PO Box 513, NL-5600 MB Eindhoven http://www.win.tue.nl/~wstomv/ | The Netherlands

Reply by email

Jonas Maebe
to Tom Verhoeff
Tom Verhoeff wrote 8 lines
On Mon, Oct 19, 2009 at 10:21:01AM +0200, Jonas Maebe wrote: > > http://www.freepascal.org/docs-html/rtl/math/setexceptionmask.html > > Fully cross-platform, even. How new is that?

Not very. I think it exists at least since 2.0.0.

Show quoted text 31 lines
It works 'partly'. Consider the program program DivideByZero; { to see whether division by zero can result in Infinite values } uses Math; var one, zero, result: Double; begin SetExceptionMask ( [ exZeroDivide{, exPrecision} ] ); one := 1.0; zero := 0.0; result := one / zero; writeln ( result ); { Expected output: +Inf } result := one / result; writeln ( result ); { Expected output: +0.0 } end. Outputs: +Inf An unhandled exception occurred at $000060B0 : EDivByZero : Division by zero $000060B0 $00005850 $0001889B In order to allow 1 / +Inf, you also need to mask exPrecision (i.e. uncomment it from the set passed to SetExceptionMask). I am surprised that division by an Infinity raises EDivByZero: Division by zero

It probably depends on your platform. In case it's Mac OS X on x86: floating point exception reporting via Unix signals isn't exactly its forte. The Darwin signal handler contains code to decode the instructions where a floating point exception occurs, because the system generates a "generic" floating point exception on integer div- by-zero. Maybe the above operation also causes a generic floating point exception which is then wrongly classified by us as div-by-zero.

In general, relying on these exceptions to return a particular value is a bad idea though. Using Unix signals for this purpose is not supported at all on Mac OS X (we should actually use Mach exceptions instead), because the state you get in the Unix signal handler is transient (if another exception happens at the same time in another thread, you may lose one exception, you may get a state that contains a mix of both exceptions, etc). Moreover, both the format of the context parameter of Unix signals and the interface for Mach exceptions are still considered SPI (system private interface) by Apple, so at this point there is no way to implement either in a way that's guaranteed to be forward compatible.

Jonas

Reply by email

Jonas Maebe
to Jonas Maebe
Jonas Maebe wrote
In case it's Mac OS X on x86: floating point exception reporting via Unix signals isn't exactly its forte.

And in case Mac OS X on ppc: the same caveats apply as for x86, except that we don't have to any opcode decoding since the PPC doesn't raise a hardware exception for integer div-by-zero. The system does appear to raise the wrong exception for your test program though, but there's not much we can do about that. This is basically our signal handler:

   case sig of
     SIGFPE :
       begin
         Case Info^.si_code Of
           FPE_FLTDIV,
           FPE_INTDIV : Res:=200;  { floating point divide by zero }
           FPE_FLTOVF : Res:=205;  { floating point overflow }
           FPE_FLTUND : Res:=206;  { floating point underflow }
           FPE_FLTRES,             { floating point inexact result }
           FPE_FLTINV : Res:=207;  { invalid floating point operation }
           Else
             Res:=207; {coprocessor error}
         end;

If the system say its FPE_FLTDIV (which according to /usr/include/sys/ signal.h means "[XSI] floating point divide by zero"), we report that...

Jonas

Reply by email

Bart
to Jonas Maebe

Jonas,

Could you comment on my opinion that in case of a floating point divide by zero error an EZeroDivide should be raised (or no exception at all depending on the FPU exception mask) and only in case of integer divide by zero (div operation) an EDivByZero should be raised?

This was my original question and no one seems to have an opinion on that.

Bart

Reply by email

Jonas Maebe
to Bart
Bart wrote 7 lines
Could you comment on my opinion that in case of a floating point divide by zero error an EZeroDivide should be raised (or no exception at all depending on the FPU exception mask) and only in case of integer divide by zero (div operation) an EDivByZero should be raised? This was my original question and no one seems to have an opinion on that.

I don't have an opinion on that either. I'm not sure how it would be technically possible with our current setup, as SysUtils intercepts run time errors and then converts these into exceptions based on their number. Since all divide-by-zero run time errors are 200, there's no way for sysutils to differentiate between the two at this time (which is probably the reason why it always raises EDivByZero).

Jonas

Reply by email

Bart
to Jonas Maebe

Jonas,

I might be just babbling, since I really have no idea how it's done, but:

In the code that "translates" the "you did something nasty" to runerror(some_exit_code) it may be possible to differentiate between float and integer fault?
This info could then be stored in some variable/object that can be used by the code that intercepts the run-time error and raises the actual fpc exception?

Bart

Reply by email

Gustavo Enrique Jimenez
to Tom Verhoeff: It would be nicer if one had the ability to make floating-point division by zero return…

2009/10/18 Tom Verhoeff :

Show quoted text 11 lines
A simple example is the situation where one needs to calculate the replacement resistor value R for parallel resistors having values R1, R2, ..., Rk.  The formula is R = 1/(1/R1 + 1/R2 + ... + 1/Rk). The formula gives a divide-by-zero if one of the resistors has value 0. But in that case, the replacement value R also equals 0.  When allowing infinities, it just works out fine (infinity + x = infinity, 1/infty = 0). That is precisely why IEEE 754 has infinities.  Also see        <http://www.cs.berkeley.edu/~wkahan/ieee754status/why-ieee.pdf> for other examples and further motivation.

Mmm... the formula R = 1/(1/R1 + 1/R2 + ... + 1/Rk) is only valid if none of Rn=0.
Programmers can and must take care of that situation.

Gustavo

Reply by email

Frank Peelo
to Gustavo Enrique Jimenez
Gustavo Enrique Jimenez wrote 16 lines
2009/10/18 Tom Verhoeff : > A simple example is the situation where one needs to calculate > the replacement resistor value R for parallel resistors having > values R1, R2, ..., Rk. The formula is R = 1/(1/R1 + 1/R2 + ... + 1/Rk). > The formula gives a divide-by-zero if one of the resistors has value 0. > But in that case, the replacement value R also equals 0. When allowing > infinities, it just works out fine (infinity + x = infinity, 1/infty = 0). > That is precisely why IEEE 754 has infinities. Also see > > <http://www.cs.berkeley.edu/~wkahan/ieee754status/why-ieee.pdf> > > for other examples and further motivation. Mmm... the formula R = 1/(1/R1 + 1/R2 + ... + 1/Rk) is only valid if none of Rn=0. Programmers can and must take care of that situation.

Agree: treating 1/0 as you would treat a finite number makes me feel uncomfortable. See for instance
http://www.cocoa.uk.com/?p=63

Frank

Reply by email

Jonas Maebe
to Bart: I might be just babbling, since I really have no idea how it's done, but: In the code…
Bart wrote
In the code that "translates" the "you did something nasty" to runerror(some_exit_code) it may be possible to differentiate between float and integer fault?

It is (on some platforms with some hacking).

This info could then be stored in some variable/object that can be used by the code that intercepts the run-time error and raises the actual fpc exception?

In theory this is of course possible, but in practice this is extremely error prone due to the nature of signal handlers. Furthermore, adding extra global variables to the interface of the system unit is very much frowned upon, and this would be required since the signal handling happens in the system unit and the translation to exceptions in the sysutils unit. Another, and much easier, option would be to change the run time error number for either integer or floating point div-by-zero. But that would obviously cause its own share of compatibility problems.

Jonas

Reply by email

Bart
to Jonas Maebe

Jonas,

Show quoted text 5 lines
In theory this is of course possible, but in practice this is extremely error prone due to the nature of signal handlers. Furthermore, adding extra global variables to the interface of the system unit is very much frowned upon, and this would be required since the signal handling happens in the system unit and the translation to exceptions in the sysutils unit.

I think this is how it's done in Delphi though.

Another, and much easier, option would be to change the run time error number for either integer or floating point div-by-zero. But that would obviously cause its own share of compatibility problems.

Yes, a different runtime error is also not Delphi compatible ;-( (But probably not likely te be relied on in existing code?)

One other thought. In the code that translates the signal to a runerror, could we pass the signal info as an extra, optional parameter to the runerror procedure, or would that also be "invisible" to the sysutils unit, when intercepting runerrors?

If we cannot reliably differentiate in the signal handler between integer divide by zero and floating point divide by zero, then we'd better not try to fix it. Better be reliably inconsistent with delphi than not being able to rely on what exception is raised. In that case we should probably document this behaviour somewhere?

Bart

Reply by email

Tom Verhoeff
to Gustavo Enrique Jimenez: 2009/10/18 Tom Verhoeff : Mmm... the formula R = 1/(1/R1 + 1/R2 + ... + 1/Rk) is only…
Gustavo Enrique Jimenez wrote 16 lines
2009/10/18 Tom Verhoeff : > A simple example is the situation where one needs to calculate > the replacement resistor value R for parallel resistors having > values R1, R2, ..., Rk.  The formula is R = 1/(1/R1 + 1/R2 + ... + 1/Rk). > The formula gives a divide-by-zero if one of the resistors has value 0. > But in that case, the replacement value R also equals 0.  When allowing > infinities, it just works out fine (infinity + x = infinity, 1/infty = 0). > That is precisely why IEEE 754 has infinities.  Also see > >        <http://www.cs.berkeley.edu/~wkahan/ieee754status/why-ieee.pdf> > > for other examples and further motivation. Mmm... the formula R = 1/(1/R1 + 1/R2 + ... + 1/Rk) is only valid if none of Rn=0. Programmers can and must take care of that situation.

No, that is just the point. The "easy" formula is actually not not correct for zero resistors when adopting classical evaluation rules. However, it works out fine with infinities. Take the case of two resistors

1/(1/R1 + 1/R2) = R1 R2 / (R1 + R2)

The left-hand side is continuous at R1=0 and at R2=0, but strictly speaking not defined in those points. The formula on the right is the same for R1, R2 not zero and it does not have a problem at R1=0 or R2=0 (unless R1=R2=0). For more than two resistors, rewriting as in the right-hand side is (practically) out of the question, but the same holds.

Point is that for such rational functions, the use of infinities is well-defined and works well. Numericists want to evaluate the left-hand side, rather than doing a case analysis or rewriting it.

Tom

E-MAIL: T.Verhoeff @ TUE.NL | Dept. of Math. & Comp. Science PHONE: +31 40 247 41 25 | Technische Universiteit Eindhoven FAX: +31 40 247 54 04 | PO Box 513, NL-5600 MB Eindhoven http://www.win.tue.nl/~wstomv/ | The Netherlands

Reply by email

Tom Verhoeff
to Frank Peelo: Agree: treating 1/0 as you would treat a finite number makes me feel uncomfortable. See…
Frank Peelo wrote 21 lines
Gustavo Enrique Jimenez wrote: > 2009/10/18 Tom Verhoeff : >> A simple example is the situation where one needs to calculate >> the replacement resistor value R for parallel resistors having >> values R1, R2, ..., Rk. The formula is R = 1/(1/R1 + 1/R2 + ... + 1/Rk). >> The formula gives a divide-by-zero if one of the resistors has value 0. >> But in that case, the replacement value R also equals 0. When allowing >> infinities, it just works out fine (infinity + x = infinity, 1/infty = 0). >> That is precisely why IEEE 754 has infinities. Also see >> >> <http://www.cs.berkeley.edu/~wkahan/ieee754status/why-ieee.pdf> >> >> for other examples and further motivation. > > Mmm... the formula R = 1/(1/R1 + 1/R2 + ... + 1/Rk) is only valid if > none of Rn=0. > Programmers can and must take care of that situation. Agree: treating 1/0 as you would treat a finite number makes me feel uncomfortable. See for instance http://www.cocoa.uk.com/?p=63

One final comment here. The field of scientific computing, including numerical methods, is a specialism by itself, and there are many misunderstandings about it. Respected experts from this field (including William Kahan) have developed a floating-point standard and have been able to convince hardware builders to implement this standard in their (co)processors. Compiler writers would do well to trust those experts, especially if they do not understand them.

So, yes 1/0 is not a finite number, but a (signed) infinity: 1/+0 = +Inf, 1/-0 = -Inf. Yes, there are two 0s among the floating-point numbers, and they are all well-behaved, and some operators work (1 + +Inf = +Inf), and others do not (+Inf + -Inf is undefined). With good reasons.

Not everybody needs to be a floating-point expert, but compiler users that do know about this, would want to use the IEEE 754 standard, because it makes life so much simpler for them. That may be the reason why FORTRAN is still used in scientific computing (robust numerical libraries and good support in the compiler; but otherwise not a language I can like).

Tom

E-MAIL: T.Verhoeff @ TUE.NL | Dept. of Math. & Comp. Science PHONE: +31 40 247 41 25 | Technische Universiteit Eindhoven FAX: +31 40 247 54 04 | PO Box 513, NL-5600 MB Eindhoven http://www.win.tue.nl/~wstomv/ | The Netherlands

Reply by email