Re: Numerical equality bug

 new new list compose Reply to this message Top page
Attachments:
+ (text/plain)

Delete this message
Author: Kaz Kylheku
Date:  
To: vapnik spaknik
CC: txr-users
Subject: Re: Numerical equality bug
On 2022-02-05 10:02, vapnik spaknik wrote:
> This looks serious:
>
> 1> (- 1183.68 9.07)
> 1174.61
> 2> (= (- 1183.68 9.07) 1174.61)
> nil
> 3> (/= (- 1183.68 9.07) 1174.61)
> t
> 4> (< (- 1183.68 9.07) 1174.61)
> nil
> 5> (> (- 1183.68 9.07) 1174.61)
> t



If you try it in C and various languages, you get the same thing.

https://stackoverflow.com/questions/588004/is-floating-point-math-broken

In TXR Lisp, I set the default floating point printing precision to 15
digits,
on purpose, so that most floating-point numbers "look good" due to being
rounded off to the decimal value you expect.

If you crank it up to 17 or more, you can see what is going on with
the least significant bits of the 52 digit manitssa:

This is the TXR Lisp interactive listener of TXR 273.
Quit with :quit or Ctrl-D on an empty line. Ctrl-X ? for cheatsheet.
TXR is made with 75% post-consumer recycled cons cells.
1> *print-flo-precision*
15
2> (set *print-flo-precision* 17)
17
3> (- 1183.68 9.07)
1174.6100000000001
4> 1174.61
1174.6099999999999

You can see that the calculated 1174.61 is not the same number as the
one
converted from a constant.

Incidentally, 15 is the value of the DBG_DIG constant in C. 15 is the
number
of decimal digits that the double type is guaranteed to preserve. If you
convert
any 15 digit decimal number (that is in range of double, exponent-wise),
to
double, then if you convert that double value back to decimal, and round
off
to 15 digits you will get your original digits.

If we make that 15 the default rounding for general printing of floats,
then
we get nice looking numbers; but of course, we get a the problem that
1174.61
aliases to more than one representation, which are not equal, but look
the
same when printed.

If floating-point, you have to be very careful when you use exact
equality
(e.g. as the condition for terminating a calculation or whatever).

It's possible to have a = function which does the epsilon range check:
two
numbers are close if they are within some small epsilon value of each
other,
but I think that "dragons be there". Such a thing is no longer an
equivalence
relation: you will get the problem that (and (= A B) (= B C)) yet
(= A C) is false. There is also the question of what is a good epsilon
to fit
most situations.

Cheers ...