Float comparison

A

Alessio Ribeca

Hi,

I know that you must keep some advices about comparing floating-points
values for equality, but why I cannot use this method ?

//
#include <stdio.h>
#include <stdbool.h>

//
#define PREC double

//
inline bool IsFloatEqual(const PREC a, const PREC b)
{
if ( a < b )
{
return false;
}
else if ( a > b )
{
return false;
}

return true;
}

//
int main(void)
{
PREC a = 0.0000001239995;
PREC b = 0.0000001239996;

printf("a = %f, b = %f\n", a, b);

printf("This numbers are%s equal!\n", IsFloatEqual(a, b) ? "": " not");

return 0;
}

Thank you, Alessio.
 
G

Guest

[I've trimmed some empty comments that just seemed to bulk out the
listing]
I know that you must keep some advices about comparing floating-points
values for equality, but why I cannot use this method ?

what does "keep some advice" mean?

#define PREC double

what's this for? It seems to obscure the code. Is PREC Italian
for "large float" or something? If so why not use a typedef?

inline bool IsFloatEqual(const PREC a, const PREC b)

what are the consts for?
{
        if ( a < b )
        {
                return false;
        }
        else if ( a > b )
        {
                return false;
        }

        return true;

}

in what way (apart from being more code) does this differ from

inline bool IsFloatEqual(const PREC a, const PREC b)
{
return a == b;
}

it suffers from the same problems in that numbers that are very close
together, that may be "logically" identical compare unequal.

int main(void)
{
        PREC a = 0.0000001239995;
        PREC b = 0.0000001239996;

        printf("a = %f, b = %f\n", a, b);

        printf("This numbers are%s equal!\n", IsFloatEqual(a, b) ? "": " not");

        return 0;

}

consider this (I converted your code to C89)

**********************************************


#include <stdio.h>

/* C89 version */
int IsFloatEqual(const double a, const double b)
{
if ( a < b )
{
return 0;
}
else if ( a > b )
{
return 0;
}

return 1;
}


void may_not_work ()
{
double x;
int count = 0;

for (x = 0.0; !IsFloatEqual(x, 1.0); x += 0.1)
{
printf ("%f ", x);
count++;
if (count == 5)
printf ("\n");
}
}

int main (void)
{
may_not_work();
return 0;
}


**********************************************

this doesn't terminate on my machine.
 
I

Ian Collins

Richard said:
What not return based on if it worked or not? Or at least use standard
prescribed macro/constant values.

0 is a standard prescribed return constant value.
 
B

Beej Jorgensen

Alessio Ribeca said:
Thank you, How can I check for equality ?

You can just use ==, but the problem you've heard of is that after doing
some math, you might not get the answer you'd expect. For instance, I
just wrote a test program that calculates a value of 1.1, but that value
actually compares greater-than 1.1!

Here's a web page I found that talks about some of the details and
solutions:

http://realtimecollisiondetection.net/blog/?p=89

And more information than you ever wanted can be found in this paper:

http://www.validlab.com/goldberg/paper.pdf

-Beej
 
J

James Kuyper

Alessio said:
Hi,

I know that you must keep some advices about comparing floating-points
values for equality, but why I cannot use this method ?

//
#include <stdio.h>
#include <stdbool.h>

//
#define PREC double

//
inline bool IsFloatEqual(const PREC a, const PREC b)
{
if ( a < b )
{
return false;
}
else if ( a > b )
{
return false;
}

return true;
}

The fundamental problem is that IsFloatEqual is essentially identical to
==, and therefore has all of the same problems, while being much more
complicated. If you declare it inline a decent compiler might optimize
IsFloatEqual(x,y) down to the same code it would generate for x==y, but
a bad compiler might generate code that wastes a lot of space and time,
to no benefit.

The fundamental problem is that every floating point operation carries
the possibility of roundoff error. As a result, two quantities that are
supposed to be equal cannot be counted on to compare exactly equal. For
instance, 3.0*(1.0/3.0) is not guaranteed to compare equal to 1.0, and
on most systems it will not.

In general, you will have to estimate the size of the errors that might
have occurred, and compare two numbers with a tolerance:

fabs(a-b) < tolerance

Figuring out what the tolerance should be is complicated even in the
simple cases, and very complicated in the general case. The tightest
reasonable value is

tolerance = (fabs(a) > fabs(b) ? fabs(a) : fabs(b)) * DBL_EPSILON;

You'll generally want a much larger value than that, depending upon what
processing your numbers have gone through before being compared.

Keep in mind that, while comparing with a tolerance may be necessary in
order to ensure that two quantities that should be equal compare equal,
it has a cost: it will also allow quantities that shouldn't compare
equal to be incorrectly identified as equal; that's an inherent
unavoidable cost of using floating point operations - if it's an
unacceptable cost, you'll have to find some other way to do your
calculations.
 
G

Guest

(e-mail address removed) writes:



What's wrong with removing a line above here?

nothing, but then I don't think it gains much either. A little
nit picky don't you think?
       if(++count==5)

and we both missed that count should be set back to zero here

Inconsistency in the layout technique is poor style. Either use
unnecessary braces or don't. But be consistent.

in what way was I inconsistent? I usually omit unnecessary braces.

What not return based on if it worked or not? Or at least use standard
prescribed macro/constant values.

0 is standard. It is equivalent to return EXIT_SUCCESS, but shorter
[1].
This is a noddy program to demonstrate a point the return 0 is there
mostly
to suppress a compiler diagnostic. In what way can a program that
doesn't
terminate "work"?

[1] I'll admit to some inconsistency here. I always use NULL
I sometimes use '\0' and I'm about 50/50 on EXIT_SUCCESS.
But "real" programs don't use EXIT_SUCCESS very much.

<snip>
 
K

Keith Thompson

Alessio Ribeca said:
Thank you, How can I check for equality ?

Depending on what you mean, there are at least three possible valid
answers:

1. Use "==".
2. You can't.
3. Define what it means for two numbers to be "equal" (or rather to be
"close enough"). Defining it is the tricky part; coding it should
be easy once you've done that.

Some people will tell you that "==" on floating-point values is
meaningless, and you should never use it. That's incorrect. It does
have a meaning; it's just that the meaning may or may not be useful,
depending on what you're trying to do.

A common approach is to test whether two numbers are close enough.
One problem with that is that you'll almost certainly end up with
cases where x and y are "equal", and y and z are "equal", but x and z
are not. If you manage to avoid that problem, you'll almost certainly
have cases where numbers that really are close enough compare unequal.

If you can modify your algorithm so it doesn't depend on
floating-point equality, do so. If not, section 14 of the comp.lang.c
FAQ, <http://www.c-faq.com/>, is a good place to start.

If you're really ambitious, you might want to read David Goldberg's
classic paper "What Every Computer Scientist Should Know About
Floating-Point Arithmetic" (Google it), but this goes far beyond what
you're asking about.
 
N

Nate Eldredge

in what way (apart from being more code) does this differ from

inline bool IsFloatEqual(const PREC a, const PREC b)
{
return a == b;
}

it suffers from the same problems in that numbers that are very close
together, that may be "logically" identical compare unequal.

They will also behave differently if asked to compare a NaN with
itself. But that's a separate issue.
 
F

Flash Gordon

Han said:
I have the nagging thought that I'm missing something here, but
comparing for equality doesn't just mean using `==' to do the
comparison. Your method is still comparing for equality, in that
an indirect way of checking for a == b is to show that a < b is
false and b < a is false (this is known as the trichotomy law and
is often used in proofs).

You and a few others have this wrong. That trichotomy not true in C or
in a number of other programming languages. Specifically it is not true
if you have a non-signalling NaN, since if one of the variable is a
non-signaling NaN you get a<b, b<a and b==a all being false.

You've been suggesting in the past that people can't point out any
incorrect advice you have given, well, here is an example.

Oh, and as you consider me to be one of the "regs" I could consider some
of the exchanges between you and Kenny as insulting to me, exchanges
where the last response was yours agreeing with Kenny. So you have been
insulting towards me before I have been insulting towards you since you
offered your challenge. So I see no reason not to go back to ignoring
you most of the time (I don't use a killfile, and reserve the right to
read and respond if I feel so inclined).
 
B

Ben Pfaff

Mark McIntyre said:
if ((x-y) < SMALL_DIFFERENCE) return true;

I'd suggest adding a "fabs" in there, at the very least.
(Threads from the past several years will suggest other
improvements also.)
 
K

Keith Thompson

Mark McIntyre said:
Ok, but don't use #defines or typedefs in this way, they only make
your code hard to read and more error prone.

I agree that #defines are in appropriate, but I see nothing wrong with
using a typdef, particularly if there's a possibility that you might
want to change from double to float, or from double to long double,
later on.
You can't. You can only see if two floats are very close to each other.

pseudocode:

if ((x-y) < SMALL_DIFFERENCE) return true;

Checking the absolute magnitude of the difference might make sense for
some applications, depending on the meaning of the numbers. But note
that a difference of 0.001 vanishes entirely for very large numbers,
and so huge as to be meaningless for very small numbers.

Defining what it means for two floating-point numbers to be "very
close" is the hard part, and it can't be done without more information
about where the numbers came from and what they represent. Once
you've done that, coding the comparison in C should be
straightforward.
 
C

CBFalconer

Alessio said:
I know that you must keep some advices about comparing floating-
points values for equality, but why I cannot use this method ?

Because floating point values are not exact. They always simply
assert they mean a value between V (the value printed) - x_EPSILON
and V + x_EPSILON. Replace x by FLOAT or DOUBLE, depending on type
used. The EPSILON values are found in float.h.

You can use an == opcode, but it will signal differences which you
don't care about. It makes more sense to compute V - value and see
if that result is less than V * x_EPSILON. If so, you can usually
conclude that the values are essentially equal.
 
K

Keith Thompson

Mark McIntyre said:
Um, in what applications would it not make sense?

For example, any application dealing with measurements that have a
precision of, say, +/-1%. So 100.0 and 100.5 would be "nearly equal",
as would 0.01 and 0.01005, but 0.01 and 0.51 would not. In this case,
it might make sense to look at the ratio rather than the difference
(but watch out for 0.0).

I'm certainly not suggesting that checking the ratio is always better
than checking the difference. It may or may not be.
 
K

Keith Thompson

CBFalconer said:
Because floating point values are not exact. They always simply
assert they mean a value between V (the value printed) - x_EPSILON
and V + x_EPSILON. Replace x by FLOAT or DOUBLE, depending on type
used. The EPSILON values are found in float.h.

The EPSILON values are meaningful only if the values being compared
are near 1.0. For example, DBL_EPSILON is the difference between 1.0
and the next representable value above 1.0.

Consider this program:

#include <float.h>
#include <stdio.h>
int main(void)
{
double x = 1.0e-100;
double y = 4.0e-100;

printf("DBL_EPSILON = %g\n", DBL_EPSILON);
printf("DBL_MIN = %g\n", DBL_MIN);

printf("x == %g\n", x);
printf("y == %g\n", y);
printf("x and y differ by a factor of %g\n", y / x);
printf("x and y differ by DBL_EPSILON * %g\n", (y - x) / DBL_EPSILON);

return 0;
}

and its output on a typical system:

DBL_EPSILON = 2.22045e-16
DBL_MIN = 2.22507e-308
x == 1e-100
y == 4e-100
x and y differ by a factor of 4
x and y differ by DBL_EPSILON * 1.35108e-84
You can use an == opcode, but it will signal differences which you
don't care about.

How do you know what differences he does or doesn't care about?
It makes more sense to compute V - value and see
if that result is less than V * x_EPSILON. If so, you can usually
conclude that the values are essentially equal.

Not always; it depends on the range of values, how they were computed
or measured, and the requirements of the individual application -- as
we've already discussed at some length.
 
B

BartC

CBFalconer said:
Because floating point values are not exact.

Surely every representable floating point value is exact?
They always simply
assert they mean a value between V (the value printed) - x_EPSILON

And every printed value is also exact. It's just they may not be an exact
correspondence between the two sets. Not using decimal printing anyway.
 
J

James Kuyper

Mark said:
The clue was in the word "pseudocode" that you snipped... :)

The fact that it's psuedocode doesn't convert parentheses into an
absolute magnitude operation. At a minumum, if you don't want to specify
fabs(x-y), at least use |x-y|.
 
K

Keith Thompson

BartC said:
Surely every representable floating point value is exact?

It depends on how you interpret it.

Given:

double x = 1.0;

what does that value 1.0 (however it's represented) actually *mean*?
Does it represent the exactly mathematical value 1, no more, no less?
Or does it represent the range of real values for which (double)1.0 is
the nearest representable approximation? Or does it represent some
quantity in the range 0.995 to 1.005, based on the precision of the
instrument used to obtain the figure?

I don't know, and neither do you, *unless* you're familiar with the
application that's using that value.
 
J

James Kuyper

Mark said:
Um, in what applications would it not make sense?

It's an unnecessary complication if it happens to already be known that
x - y is not negative. It's a rare occurrence, but I have had the
occasion to take advantage of that simplification.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top