Jump to content
ŁukaszDe

Regression - Delphi 12 - IsZero()

Recommended Posts

Posted (edited)

Hi

 

I found today a problem with IsZero method:

Here is comparison last version of D11 and D12:

 

image.thumb.png.e0d8d925351e6d5c0cdb6f529d961cb2.png

Edited by ŁukaszDe

Share this post


Link to post

As far as I can see there are no changes in the implementation. Also a quick console example shows no regression. Can you please provide the source for your test case to verify?

 

BTW, could it be only a problem with Watch Variables?

  • Thanks 1

Share this post


Link to post

Right, Watch list showing False but in code it is entering to if statement with IsZero(czas_przejazdu, 1e-3) where czas_przejazdu = 0.

Share this post


Link to post
Posted (edited)

Floating point is always fun.

 

Math.IsZero has 3 overloads for different types - extended, double, single

 

With a simple test app on Delphi 12:

 

program Project1;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.Math,
  System.SysUtils;

procedure Test;
var
  e: extended;
  d: double;
  s: single;
  te, td, ts: boolean;
begin
  e := 3.5527136788055E-15;
  d := 3.5527136788055E-15;
  s := 3.5527136788055E-15;
  te := iszero(e);
  td := iszero(d);
  ts := iszero(s);
end;

begin
  try
    Test;
  except
    on e: Exception do
      Writeln(e.ClassName, ': ', e.Message);
  end;

end.

On Win32, I was surprised that 'te' is false. 

On Win64, the values are as expected.

 

Something to note for those that don't know. In Win64, 'extended' is 64bit like 'double', where on Win32 it is 80bit.

 

I didn't check between versions, but is the resolution the same?

 

Squirrelled away on line 5290 in System.Math, there is:

const
  FuzzFactor = 1000;
  SingleResolution   = 1E-7 * FuzzFactor;
  DoubleResolution   = 1E-15 * FuzzFactor;
{$IFDEF EXTENDEDHAS10BYTES}
  ExtendedResolution = 1E-19 * FuzzFactor;
{$ELSE !EXTENDEDHAS10BYTES}
  ExtendedResolution = DoubleResolution;
{$ENDIF !EXTENDEDHAS10BYTES}

IMO I think those consts should probably be variables and in the interface section so they can be overridden if required.

 

It does look like something buggy happening with the 'Extended' type on win32...

 

I played with SameValue and Compare( which uses SameValue), which didn't look bad. SameValue/Compare take an Epsilon (the resolution).

 

Stepping through IsZero, what I spotted in the debugger:

  if Epsilon = 0 then
    Epsilon := ExtendedResolution;

After the assignment to Epsilon, Epsilon was 0 and not 1e-16. On 64bit, Epsilon becomes 1e-12.

 

So something funky with the codegen? I recall some changes to FP operations in the new compiler, but I can't recall offhand.

Edited by darnocian

Share this post


Link to post
Posted (edited)

I was looking at docs: https://docwiki.embarcadero.com/Libraries/Athens/en/System.Extended

 

I'm not a fan of the type being so different between platforms. My memory is still 64kb, so am relying too much on ChatGPT these days 😉  There is float32 and float64... maybe making a few more types indicating how many bits can also help make the size a bit more obvious.

Edited by darnocian

Share this post


Link to post

OK here's what I don't get. Seeing this

const
  FuzzFactor = 1000;
  SingleResolution   = 1E-7 * FuzzFactor;
  DoubleResolution   = 1E-15 * FuzzFactor;

I have to ask myself, why not

 

const
  SingleResolution   = 1E-4;
  DoubleResolution   = 1E-12;

Sure the calculation happens only once, and FuzzFactor seems to explain a magic number but then we still have those other magic numbers... just an observation.

Share this post


Link to post

The best way to deal with this is never to call any of these functions in the first place, they are all useless

  • Thanks 1

Share this post


Link to post
49 minutes ago, Sherlock said:

we still have those other magic numbers

Actually, they are not that magic:

image.thumb.png.8d4d30ebd0ba81ac683083d83364842e.png

  • Thanks 1

Share this post


Link to post
1 hour ago, David Heffernan said:

The best way to deal with this is never to call any of these functions in the first place, they are all useless

What do you mean by useless? Just not liking the specific implementation? 

Share this post


Link to post
51 minutes ago, darnocian said:

What do you mean by useless? Just not liking the specific implementation? 

This entire approach of applying some arbitrary epsilon is rubbish. I can't imagine any scenarios where they'd be useful and I've only ever seen them used inappropriately.

Share this post


Link to post
2 minutes ago, David Heffernan said:

This entire approach of applying some arbitrary epsilon is rubbish.

I beg to differ here. The approach is to have a decent epsilon when no one is given. It doesn't prohibit anyone to give an epsilon appropriate to the current scenario.

 

12 minutes ago, David Heffernan said:

I've only ever seen them used inappropriately

The default values currently used actually cover a common beginner's mistake assuming that floats can be compared for exact equality. Usually the IsZero implementation works pretty good for the majority of the cases - at least those I encountered myself. Perhaps you just never stumbled about code using them appropriately?

  • Like 1

Share this post


Link to post
10 minutes ago, Uwe Raabe said:

The default values currently used actually cover a common beginner's mistake assuming that floats can be compared for exact equality.

Floats can be compared for exact equality, in plenty of circumstances. The beginner mistake is to use some epsilon value without any sound rationale for it.

10 minutes ago, Uwe Raabe said:

Usually the IsZero implementation works pretty good for the majority of the cases

Usually, and works pretty good, and for the majority of cases doesn't sound great with my numerical programming head on.

 

 

Share this post


Link to post
Posted (edited)

The rationale for it is when it comes to rounding due to the way the float is represented with limited bits... there will be to mismatches. You may be lucky, and comparison may work, but the epsilon comparison approach is a failsafe method to cater for rounding issues in the process of various calculations.

 

Here is a simple example.

procedure Test;
var
  d: double;
  d2: double;
  diff: double;
  v, v2: double;
begin
  d :=  1.0000002;
  d2 := 1.0000001;
  v  := 0.0000001;
  v2 := 0.0000001;
  diff := d - d2;  // 9.99999998363421e-08

  assert(v = v2); // values do match
  assert(diff <> v); // doesn't match because of rounding
  assert(SameValue(diff, v));
end;

above done on win32. 

 

I aligned the numbers on purpose... if we do the math, we can see v should be d-d2. 

 

 v = v2 is true....

 

then noddy calculation, we have diff = d-d2... and diff <> v,

 

but SameValue with the epsilon shows with the given precision, we assume the numbers to be the same.

 

 

Edited by darnocian

Share this post


Link to post
7 hours ago, darnocian said:

The rationale for it is when it comes to rounding due to the way the float is represented with limited bits... there will be to mismatches. You may be lucky, and comparison may work, but the epsilon comparison approach is a failsafe method to cater for rounding issues in the process of various calculations.

I know how floating point math works, it's been what I've done for a living for the past 30 years. 

 

Using arbitrary epsilon values is not failsafe and relies on luck. 

Share this post


Link to post
22 minutes ago, David Heffernan said:

I know how floating point math works, it's been what I've done for a living for the past 30 years. 

 

You wanted to make your math lib available for the public, back in the days. Still waiting 😛

Share this post


Link to post
1 hour ago, Attila Kovacs said:

You wanted to make your math lib available for the public, back in the days. Still waiting 😛

I don't have a math lib that is publishable. I've never wanted to have or do such a thing. You must be mis-remembering.

Share this post


Link to post
7 hours ago, David Heffernan said:

I know how floating point math works

It isn't all about you. I'm just sharing. Glad you know.

Share this post


Link to post
Posted (edited)
7 hours ago, David Heffernan said:

Using arbitrary epsilon values is not failsafe and relies on luck. 

mainly on what solution thinks is acceptable.

 

These are all tools. we can choose to use what we want, but just need to know the limitations.

Edited by darnocian

Share this post


Link to post
23 hours ago, David Heffernan said:

This entire approach of applying some arbitrary epsilon is rubbish. I can't imagine any scenarios where they'd be useful and I've only ever seen them used inappropriately.

What's your approach?

Share this post


Link to post
6 hours ago, David Heffernan said:

I don't have a math lib that is publishable. I've never wanted to have or do such a thing. You must be mis-remembering.

I read something in so from you but I can't find it, it was about the whole rtl float handling is rubbish or something like that. Not sure about the details.

Share this post


Link to post
1 hour ago, Attila Kovacs said:

I read something in so from you but I can't find it, it was about the whole rtl float handling is rubbish or something like that. Not sure about the details.

Opinions are easy. Code... a bit harder.

  • Haha 1

Share this post


Link to post
3 hours ago, Attila Kovacs said:

I read something in so from you but I can't find it, it was about the whole rtl float handling is rubbish or something like that. Not sure about the details.

Well, that's true. Lots of thread safety issues with how it handles floating point control status. But that's not that same as having a math lib. 

Share this post


Link to post

I'm still interested to know more about any alternatives to using epsilon for comparison. I've only seen subtle variations on the same theme, and always keen to learn something new.

Share this post


Link to post
21 minutes ago, darnocian said:

I'm still interested to know more about any alternatives to using epsilon for comparison. I've only seen subtle variations on the same theme, and always keen to learn something new.

It depends on what you are comparing, what algorithms are involved etc. 

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

×