Tugger the SLUGger!SLUG Mailing List Archives

Re: Floating point comparisons, a <= b <= c operations in Python (was Re: [coders] Python newbie question


On Thu, 2006-11-16 at 10:46 +1100, Jamie Wilkinson wrote:

> What's better:
> 
>  if expected - epsilon <= computation()<= expected + epsilon:
> 
> versus:
> 
>  if abs(computation() - expected) >= epsilon:

Epsilon if a function of the number of bits in the mantissa, but not the
number of bits of exponent.  So a better test tends to be

  if (fabs(computation()/expected) > 1 + epsilon)
    blah

but that makes matters worse, because the division loses more precision,
so the better but less obvious C code is

  int expected_exponent;
  frexp(expected, &expected_exponent);
  double scaled_epsilon = ldexp(unscaled_epsilon, expected_exponent);
  if (fabs(computation() - expected) >= scaled_epsilon)
    blah

where the "unscaled_epsilon" is for the case where you are comparing 1
against 1, and the scaled_epsilon adjusts for the expected magnitude.
Note that ldexp and frexp tend to be very fast (compared to a floating
point subtraction), because they are integer operations with a little
bit bashing.

Fabs is fast, too, because in IEEE floats, it simply clears a singe bit.
Many other ancient pre-IEEE-std floating point formats have the same
property.

So, yes, fabs() and a single compare is faster than two comparisons,
because the comparisons do two relatively expensive (compared to fabs)
floating point subtractions, not just one.

It is worth noting that

  expected + unscaled_epsilon == expected

when the exponent of "expected" exceeds the number of bits in the
mantissa.  But is degraded and not very useful long before then.