Regarding Q. 14-5

B

Brian Dude

Hello, I understand the part about not comparing two floating-point
numbers for exact-ness. Does this also apply to comparisons to constants?
i.e.:

double f;

if ( f <= 1.0){
...
}

TIA,
Brian
 
P

pete

Brian said:
Hello, I understand the part
about not comparing two floating-point
numbers for exact-ness.

Your example doesn't show it.
There's nothing wrong with comparing two doubles
with a relational operator.
 
F

Flash Gordon

pete said:
Your example doesn't show it.
There's nothing wrong with comparing two doubles
with a relational operator.

However, something like
if (f == CONSTANT)

would be bad, because:
1) If f is calculated (and why else would it be a variable) it might not
be exactly CONSTANT even if an infinite precision system said it was.
2) For some values CONSTANT won't be exactly what you think.
 
M

Mark McIntyre

Your example doesn't show it.
There's nothing wrong with comparing two doubles
with a relational operator.

Except that even when equal as far as expectation is concerned, they
might compare unequal.

#include <stdio.h>
#include <math.h>

int main(void)
{
double x = acos(0);

x=cos(((x*123.4567)-100)/123.4567 +100/123.4567);

printf("%s\n", x==0.0?"equal":"different");

return 0;
}

Since at least one of the two objects being compared cannot be a
constant, yes. Obviously you're a bit safer with lessthan/morethan but
you could still be surprised - delta can be both positive and negative
 
P

pete

Mark said:
Except that even when equal as far as expectation is concerned, they
might compare unequal.
printf("%s\n", x==0.0?"equal":"different");

== is an equality operator.
== is not a relational operator.

<= is a relational operator.
<= is not an equality operator.
 
M

Malcolm

Brian Dude said:
Hello, I understand the part about not comparing two floating-point
numbers for exact-ness. Does this also apply to comparisons to constants?
i.e.:

double f;

if ( f <= 1.0){
...
}
That is dangerous.
f may be mathematically equal to 1, but slightly bigger because of floating
point unit errors (eg if it is the length of a unit vector, calculated with
a call to sqrt()).
 
E

Eric Sosman

Malcolm said:
That is dangerous.
f may be mathematically equal to 1, but slightly bigger because of floating
point unit errors (eg if it is the length of a unit vector, calculated with
a call to sqrt()).

Malcolm raises an important problem, but does not show
how to solve it. The solution is to compare not to 1.0 but
to 1.0 plus a suitable error tolerance:

if ( f <= 1.0 + epsilon )

Unfortunately, this is just as dangerous as the original,
because f might be mathematically equal to 1+epsilon but
slightly bigger because of floating point errors. To allow
for that possibility, the test should be written as

if ( f <= 1.0 + 2.0 * epsilon )

This is still dangerous, because f might be mathematically
equal to 1+2*epsilon but slightly bigger because of floating
point errors. The same objection applies to 1+N*epsilon for
any finite N, so the only recourse is to allow for a possibly
infinite amount of inaccuracy in the floating-point calculation:

#include <math.h>
...
if (f <= 1.0 + INFINITY )

This solution should meet Malcolm's objections.
 
F

Flash Gordon

Eric said:
Malcolm raises an important problem, but does not show
how to solve it. The solution is to compare not to 1.0 but
to 1.0 plus a suitable error tolerance:

if ( f <= 1.0 + epsilon )

<snip>

You forgot the possibility that f may be mathematically slightly bigger
than 1.0 but due to floating point errors be slightly smaller, to avoid
this you should do:
if (f <= 1.0-epsilon) {
/* it's smaller or equal */
}
else if (f <= 1.0+epsilon) {
/* not sure */
}
else {
/* it's larger */
}
#include <math.h>
...
if (f <= 1.0 + INFINITY )

This solution should meet Malcolm's objections.

Then following through with your argument we get:
if (f <= 1.0-INFINITY ) {
/* it's smaller or equal */
}
else if (f <= 1.0+INFINITY) {
/* not sure */
}
else {
/* it's larger */
}

:)
 
M

Mark McIntyre

Malcolm raises an important problem, but does not show
how to solve it.

Eric raises an important problem with any solution, but fails to show
how to solve it :)

The solution is indeed to compare to 1+epsilon, but to define epsilon
suitably for your application. Its likely for instance that if you're
doing calcs that involve rounding to 5dp, then a comparison to 4dp
will succeed. If you're doing the sum I did earlier, then epsilon
could be 1e-10 and that'd be successful.

The precise method of determining epsilon is left as an exercise for
the reader.
 
D

Dik T. Winter

> Malcolm wrote: [ About f <= 1.0 ]
> > That is dangerous.
> > f may be mathematically equal to 1, but slightly bigger because of
> > floating point unit errors (eg if it is the length of a unit vector,
> > calculated with a call to sqrt()).

In that case we have a false negative.
> Malcolm raises an important problem, but does not show
> how to solve it. The solution is to compare not to 1.0 but
> to 1.0 plus a suitable error tolerance:
> if ( f <= 1.0 + epsilon )
> Unfortunately, this is just as dangerous as the original,
> because f might be mathematically equal to 1+epsilon but
> slightly bigger because of floating point errors.

This is different. We want to compare with 1.0, and add epsilon toe
avoid false negatives. But in this case we get a true negative.
On the other hand, if f is mathematially greater than 1+epsilon,
but slightly smaller due to floating point errors we get a false
positive...

But indeed, comparing to 1.0 + epsilon is just as silly as comparing
to plain 1.0. The bottom line is that when you want to use
floating-point you better know what you are doing so that you can
judge how you wish to compare. (In all my years of programming in
numerical mathematics I rarely, if at all, coded a line like
f <= 1.0 + epsilon, but many lines like f <= 1.0.)
 
E

Eric Sosman

Mark said:
Eric raises an important problem with any solution, but fails to show
how to solve it :)

The solution is indeed to compare to 1+epsilon, but to define epsilon
suitably for your application. Its likely for instance that if you're
doing calcs that involve rounding to 5dp, then a comparison to 4dp
will succeed. If you're doing the sum I did earlier, then epsilon
could be 1e-10 and that'd be successful.

The precise method of determining epsilon is left as an exercise for
the reader.

What I was trying to point out is that the supposed
"solution" is no solution at all. For example,

if ( f <= 1.0 )
angle = asin(f);

is not improved by replacing 1.0 with 1.0+epsilon ...

The solution -- to the degree that one exists -- is
to use care in calculating f in the first place, and to
understand the nature of the errors that occur along the
way and the manner in which they grow and decay. That
field of study is called Numerical Analysis, and is too
rich a topic to be treated at any depth in Usenet postings.

However, the fact that floating-point numbers are usually
(but not always!) approximations and that floating-point
calculations usually (but not always!) involve error should
not be taken as meaning that all floating-point comparisions
are "dangerous!"

I once read (but cannot now find) a description of a
progression of attitudes that may apply here:

1. Initially, people trust every digit of every number
a computer calculates.

2. Having learned that some (even many) digits can be
incorrect, the once-burned-twice-shy crowd overreacts
by never trusting any floating-point calculation.

3. With further experience and study, a more rational
(pun intentional) attitude of tolerance (ditto)
eventually develops. Floating-point arithmetic is
imperfect (as is int arithmetic) but like many other
tools is useful if handled with care.

It seems to me that someone whose alarm bells clang at `f<=1.0'
may not have completed the journey from stage 2 to 3. Malcolm
is probably at 2+epsilon or 3-delta, but afraid to calculate the
remaining distance ;-)

Recommended (strongly recommended) reading: "What Every
Computer Scientist Should Know About Floating-Point Arithmetic"
by David Goldberg, widely archived on the Web.
 
M

Mark McIntyre

What I was trying to point out is that the supposed
"solution" is no solution at all.

I realise that, but my point is that you're wrong. :)
For example,

if ( f <= 1.0 )
angle = asin(f);

is not improved by replacing 1.0 with 1.0+epsilon ...

Of course it is. You just need to choose epsilon carefully, and decide
how to handle failures. The choice of both is highly dependent on how
you arrived at the FP value you want to examine, and how you want to
handle the problem cases.

#define EPS -1e-12

if ( f <= 1.0+EPS )
angle = asin(f);
else
{
puts("f ->1");
f = asin(1.0);
}
Recommended (strongly recommended) reading: "What Every
Computer Scientist Should Know About Floating-Point Arithmetic"
by David Goldberg, widely archived on the Web.

Absolutely.
 
P

pete

Mark said:
Eric raises an important problem with any solution, but fails to show
how to solve it :)

The solution is indeed to compare to 1+epsilon, but to define epsilon
suitably for your application.

It depends on what you're doing.

In the very few times that I have written code
which compares a double variable against a double constant
with either a relational operator or an equality operator,
it would be inappropriate to make use of DBL_EPSILON.
The (DBL_MAX >= x && x > 0) expression, is correct as is,
and so is the (0 != x) expression.

double sssqrt(double x)
{
if (DBL_MAX >= x && x > 0) {
const double a = x;
double b = x / 2 + 0.5;

do {
x = b;
b = (a / x + x) / 2;
} while (x > b);
} else {
if (0 != x) {
errno = EDOM;
x = HUGE_VAL;
}
}
return x;
}

In reviewing my toy math library, I've noticed that
I don't have any code which compares two double variables
with an equality operator.

All of my epsilon usage is kind of like this:

static double p_i(void)
{
unsigned n;
double p, a, b;

p = 0;
a = 3;
n = 1;
do {
a /= 9;
b = a / n;
a /= 9;
n += 2;
b -= a / n;
n += 2;
p += b;
} while (b > DBL_EPSILON / 4);
a = 2;
n = 1;
do {
a /= 4;
b = a / n;
a /= 4;
n += 2;
b -= a / n;
n += 2;
p += b;
} while (b > DBL_EPSILON / 2);
return 4 * p;
}
 
B

Ben Pfaff

August Karlstrom said:

6.5.8 Relational operators
Syntax
1 relational-expression:
shift-expression
relational-expression < shift-expression
relational-expression > shift-expression
relational-expression <= shift-expression
relational-expression >= shift-expression

[...]

6.5.9 Equality operators
Syntax
1 equality-expression:
relational-expression
equality-expression == relational-expression
equality-expression != relational-expression
 
P

pete

Mark said:
Note carefully that I did not say DBL_EPSILON.

I would say that in cases
where a suitable definition of epsilon, is zero,
that the use of epsilon isn't required.
 
A

August Karlstrom

Ben said:


6.5.8 Relational operators
Syntax
1 relational-expression:
shift-expression
relational-expression < shift-expression
relational-expression > shift-expression
relational-expression <= shift-expression
relational-expression >= shift-expression

[...]

6.5.9 Equality operators
Syntax
1 equality-expression:
relational-expression
equality-expression == relational-expression
equality-expression != relational-expression

OK, that's weird. Still `==' works as a relational operator in the
common (mathematical) sense.


August
 
S

Skarmander

August said:
Ben said:
pete wrote:

== is not a relational operator.

?


6.5.8 Relational operators
Syntax
1 relational-expression:
shift-expression
relational-expression < shift-expression
relational-expression > shift-expression
relational-expression <= shift-expression
relational-expression >= shift-expression

[...]

6.5.9 Equality operators
Syntax
1 equality-expression:
relational-expression
equality-expression == relational-expression
equality-expression != relational-expression

OK, that's weird. Still `==' works as a relational operator in the
common (mathematical) sense.
Yes, it's a nice gotcha, isn't it? Like "byte", this is one of those terms C
appropriates in its own way and doesn't quite mean what you'd expect it to mean.

S.
 
P

pete

OK, that's weird. Still `==' works as a relational operator in the
common (mathematical) sense.

I wrote what I wrote, plainly:

"== is an equality operator.
== is not a relational operator.

<= is a relational operator.
<= is not an equality operator."
 

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,769
Messages
2,569,580
Members
45,053
Latest member
BrodieSola

Latest Threads

Top