Wrong results when comparing negative double variables in an if statement

J

John

Hi,

I encountered a strange problem while debugging C code for a
Windows-based application in LabWindows CVI V5.5, which led me to
write the test code below. I tried this code with a different compiler
and got the same erroneous result on two different PCs (with OS Win98
& Win98SE), so it appears to be a problem with ANSI C. I thought that
negative double variables could be compared as easily and *reliably*
as integers, but apparently not?

#include <ansi_c.h>

void main (void)
{
double a = -2.0, b = -2.0;

if (a > b)
printf("a is greater than b because a is %f and b is %f\n", a, b);
else
printf("a is not greater than b because a is %f and b is %f\n", a,
b);

a -= 0.01; // decrease value of a by 0.01
a += 0.01; // restore original value of a by increasing it by 0.01

if (a > b)
printf("a is greater than b because a is %f and b is %f\n", a, b);
else
printf("a is not greater than b because a is %f and b is %f\n", a,
b);
}

The output as copied from the emulated DOS window is:

a is not greater than b because a is -2.000000 and b is -2.000000
a is greater than b because a is -2.000000 and b is -2.000000

If I decrement and then increment a by 0.001, everything is fine, so
it doesn't look like there is a problem with the small magnitude of
the fractions.

I would be grateful for any solutions or suggestions to this problem
so that I can process *all* fractions correctly.

Thanks in advance,
John.
 
E

Eric Sosman

John said:
Hi,

I encountered a strange problem while debugging C code for a
Windows-based application in LabWindows CVI V5.5, which led me to
write the test code below. I tried this code with a different compiler
and got the same erroneous result on two different PCs (with OS Win98
& Win98SE), so it appears to be a problem with ANSI C. I thought that
negative double variables could be compared as easily and *reliably*
as integers, but apparently not?

Apparently not, indeed. This is Question 14.1 in the
comp.lang.c Frequently Asked Questions (FAQ) list

http://www.eskimo.com/~scs/C-faq/top.html

.... and the next time somebody tells you that everybody's
"computer-literate" nowadays, you can cite the frequency
of this question as evidence to the contrary.
 
G

Gordon Burditt

I encountered a strange problem while debugging C code for a
Windows-based application in LabWindows CVI V5.5, which led me to
write the test code below. I tried this code with a different compiler
and got the same erroneous result on two different PCs (with OS Win98
& Win98SE), so it appears to be a problem with ANSI C. I thought that
negative double variables could be compared as easily and *reliably*
as integers, but apparently not?

Rounding error. If you're going to use floating-point numbers,
learn to live with it. And it's not only *NEGATIVE* numbers
that have the problem.
#include <ansi_c.h>

Non-standard include file.
void main (void)

main returns int, not void!
{
double a = -2.0, b = -2.0;

if (a > b)
printf("a is greater than b because a is %f and b is %f\n", a, b);
else
printf("a is not greater than b because a is %f and b is %f\n", a,
b);

a -= 0.01; // decrease value of a by 0.01
a += 0.01; // restore original value of a by increasing it by 0.01

No, you are NOT guaranteed that this will restore a to the original
value. There is no exact value of 0.01 in binary floating point.
if (a > b)
printf("a is greater than b because a is %f and b is %f\n", a, b);
else
printf("a is not greater than b because a is %f and b is %f\n", a,
b);

Print the numbers with unreasonably large precision, say %200.100f,
and you'll see what is going on. Also try printing 0.01 with
unreasonably large precision. Note that you're doing this for
debugging purposes, not because floating-point numbers have hundreds
of digits of accuracy, which they don't on any real machines I
have encountered, barring use of bignum packages which aren't
native C types.
}

The output as copied from the emulated DOS window is:

a is not greater than b because a is -2.000000 and b is -2.000000
a is greater than b because a is -2.000000 and b is -2.000000

If I decrement and then increment a by 0.001, everything is fine, so
it doesn't look like there is a problem with the small magnitude of
the fractions.

There is no exact value of 0.001 in binary floating point, either.
I would be grateful for any solutions or suggestions to this problem
so that I can process *all* fractions correctly.

Rounding error. Learn to live with it. (Very few decimal numbers
other than exact integers have exact representations in binary
floating point, a few exceptions being .5, .25, .75, .125, .375,
..625, and .875.) You might want to do this by explicitly rounding the
number yourself, and do NOT depend on what happens at the
exactly-half-way points.

Money is best represented as an integer quantity of the smallest
unit of currency you have to deal with (which might be cents in the
USA, or might be ten-thousandths of cents if you're an electric
company setting a price per kilowatt-hour to bill customers). You
can store this in an integer or floating type as appropriate for
the application. Bill Gates wouldn't want to use a 32-bit unsigned
long in cents (overflows at slightly under $43 million) for his net
worth, but it's fine for your average kid running a lemonade stand.
double or (for C99) long long might work well for all but the biggest
companies/governments.

Gordon L. Burditt
 
D

Darrell Grainger

Hi,

I encountered a strange problem while debugging C code for a
Windows-based application in LabWindows CVI V5.5, which led me to
write the test code below. I tried this code with a different compiler
and got the same erroneous result on two different PCs (with OS Win98
& Win98SE), so it appears to be a problem with ANSI C. I thought that
negative double variables could be compared as easily and *reliably*
as integers, but apparently not?

ANSI C is fine. Your assumption about comparing double variables is wrong.

Thing about this, there are infinite numbers between 0 and 1. Therefore
there is no way a computer can represent all numbers between 0 and 1 let
alone all real numbers in a larger range. This means that there will be
some numbers that are not represented. If I have:

double a = 2;
a += 0.01;
a -= 0.01;

Maybe 2.01 is a number that your compiler cannot represent. It might
decide to round up to the next closest number. When you decrement you
probably get a number that cannot be represented so it rounds up to the
next closet number. Now a will equal 2.00000000000001. This is called
representation error.

When you print them out, printf will only print 6 digits. So it looks like
a is still 2.000000 but it has just trimmed off the representation error
from the display. If you did something like:

printf("%020.20f\n", a);

You'd see the real value of a. In <float.h> are macros to help with this
problem. Rather than comparing a to b you would look at the different
between a and b. If the different is smaller than EPSILON then you should
assume they are close enough to be called equal.
 
M

Martin Ambuhl

John said:
Hi,

I encountered a strange problem while debugging C code for a
Windows-based application in LabWindows CVI V5.5, which led me to
write the test code below. I tried this code with a different compiler
and got the same erroneous result on two different PCs (with OS Win98
& Win98SE), so it appears to be a problem with ANSI C. I thought that
negative double variables could be compared as easily and *reliably*
as integers, but apparently not?

#include <ansi_c.h>
The above is *NOT* an ANSI C header. It is non-standard.
void main (void)

It is invalid in a hosted implementation (which anything running under
Window is) for main to have *any* return type other than void.
{
double a = -2.0, b = -2.0;

if (a > b)
printf("a is greater than b because a is %f and b is %f\n", a, b);
else
printf("a is not greater than b because a is %f and b is %f\n", a,
b);

Please check the FAQ before posting. In particular, read the answers in
section 14 (floating point) <http://www.eskimo.com/~scs/C-faq/s14.html>.
If that does not suffice, remember that testing floating point numbers
for equality, which you are doing surreptitiously, is infested with
traps for the unwary.
 
K

Kelsey Bjarnason

[snips]

It is invalid in a hosted implementation (which anything running under
Window is) for main to have *any* return type other than void.

Want to try that one again? Like, maybe, by suggesting a return type of,
say, int? :)
 
J

John

Hi,

Thank you all for your help. I'll represent all my numbers as integers
and only convert them to floating point numbers after processing.

Cheers,
John.
 
E

Eric Sosman

John said:
Hi,

Thank you all for your help. I'll represent all my numbers as integers
and only convert them to floating point numbers after processing.

You have just discovered that a screwdriver is not a
claw hammer, which is useful knowledge. You have decided
therefore to drive screws with your hammer, which is a sub-
optimal response ...
 
M

Michael Wojcik

It is invalid in a hosted implementation (which anything running under
Window is) ...

A great many Windows programs run in a freestanding implementation
known as "GUI mode". Such programs enter in a function named WinMain.

This freestanding implementation is itself implemented under a
hosted implementation, but a Windows GUI-mode program's main is
part of the implementation, not part of the user program.

Of course, if you're writing a Windows program, and it includes a
main, you're almost certainly writing to the hosted implementation
and that main should conform to the spec.
 
J

John

Eric Sosman said:
You have just discovered that a screwdriver is not a
claw hammer, which is useful knowledge. You have decided
therefore to drive screws with your hammer, which is a sub-
optimal response ...

That's a good analogy, but it's how the desktop calculators and
checkout cash registers do it. The decimal point is never typed in
although it appears on the receipt and the display in its fixed
position. I only need two decimal places so I think the hammer will
suffice. :)
 
V

Villy Kruse

That's a good analogy, but it's how the desktop calculators and
checkout cash registers do it. The decimal point is never typed in
although it appears on the receipt and the display in its fixed
position. I only need two decimal places so I think the hammer will
suffice. :)

The cash register works in fixed point decimal, a common data type seen
in COBOL but not normaly supported by the C language. I do beleive
that the decimal point *is* typed in when using those machines, at least
on general purpose adding machines.


Villy
 
J

John

My parents own a retail business and we never type in a decimal point
on any of our cash registers, although such a key is present on some
models, even though it need not be used. It's unnecessary for any
currency that has 100 pence/cents/etc to the pound/dollar/euro etc.
It's faster to type in '246' rather than '2.46' (one less key to
press). That trick meets my requirements although it woundn't work for
fractions that have a variable number of decimal places.

John.
 

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,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top