views:

664

answers:

9

Possible Duplicate:
problem in comparing double values in C#

I've read it elsewhere, but really forget the answer so I ask here again. This loop seems never end regardless you code it in any language (I test it in C#, C++, Java...):

double d = 2.0;
while(d != 0.0){
   d = d - 0.2;
}
+31  A: 

Floating point calculations are not perfectly precise. You will get a rounding error so it won't be ever exactly be equal to zero. Try adding a debug statement to see the problem:

double d = 2.0;
while (d != 0.0)
{
    Console.WriteLine(d);
    d = d - 0.2;
}
2
1,8
1,6
1,4
1,2
1
0,8
0,6
0,4
0,2
2,77555756156289E-16   // Not exactly zero!!
-0,2
-0,4

One way to solve it is to use the type decimal.

Mark Byers
And why there are round errors? Type 'double' stores binary digits. Because 0,2 written binary is periodical fraction, we must cut off something to write the number on N bits.
adf88
+26  A: 

(For one thing you're not using the same variable throughout, but I'll assume that's a typo :)

0.2 isn't really 0.2. It's the closest double value to 0.2. When you've subtracted that 10 times from 2.0, you won't end up with exactly 0.0.

In C# you can change to use the decimal type instead, which will work:

// Works
decimal d = 2.0m;
while (d != 0.0m) {
   d = d - 0.2m;
}

This works because the decimal type does represent decimal values like 0.2 precisely (within limits; it's a 128-bit type). Every value involved is precisely representable, so it works. What wouldn't work would be this:

decimal d = 2.0m;
while (d != 0.0m) {
   d = d - 1m/3m;
}

Here, "a third" isn't exactly representable so we end up with the same problem as before.

In general though, it's a bad idea to perform exact equality comparisons between floating point numbers - usually you compare them within a certain tolerance.

I have articles on floating binary point and floating decimal point from a C#/.NET context, which explain things in more detail.

Jon Skeet
+1  A: 

f is uninitialised ;)

If you mean:

double f = 2.0;

This can be a effect of non-precise arthimetic on double variables.

killer_PL
+3  A: 

You are better off using

while(f  > 0.0) 

*edit : See pascal's comment below. But if you do need to run a loop an integral, deterministic number of times, rather use an integral data type.

nonnb
I know I should use it, but why f!=0.0 doesn't work?
Truong Ha
You might run once too many, if the last `f` is 2.0e-16...
pascal
+1  A: 

it's because of the precision of floating point. use while (d>0.0), or if you must,

while (Math.abs(d-0.0) > some_small_value){

}
Louis Rhys
+2  A: 

The problem is floating point arithmetic. If there is no exact binary representation for a number, then you can only store the closest number to it (just like you couldn't store the number 1/3 in decimal - you can only store something like 0.33333333 for some length of '3's.) This means that arithmetic on floating point numbers is quite often not totally accurate. Try something like the following (Java):

public class Looping {

    public static void main(String[] args) {

        double d = 2.0;
        while(d != 0.0 && d >= 0.0) {
            System.out.println(d);
            d = d - 0.2;
        }

    }

}

Your output should be something like:

2.0
1.8
1.6
1.4000000000000001
1.2000000000000002
1.0000000000000002
0.8000000000000003
0.6000000000000003
0.4000000000000003
0.2000000000000003
2.7755575615628914E-16

And now you should be able to see why the condition d == 0 never happens. (the last number there is a number that is very close to 0 but not quite.

For another example of floating point weirdness, try this:

public class Squaring{

    public static void main(String[] args) {

        double d = 0.1;
        System.out.println(d*d);

    }

}

Because there is no binary representation of exactly 0.1, squaring it does not produce the result you would expect (0.01), but actually something like 0.010000000000000002!

Stephen
"Never totally exact" is overstating it, IMO. Just because not *every* decimal value isn't exactly representable in binary floating point doesn't make the addition of the exactly-represented 0.25 and 0.25 to get exactly 0.5 any less exact for example.
Jon Skeet
Edited, thanks Jon - it was a bit overstated.
Stephen
+6  A: 

I remember buying a Sinclair ZX-81, working my way through the excellent Basic programming manual, and nearly returning to the shop when I came across my first floating point rounding error.

I'd never have have imagined that people would still be having these problems 27.99998 years later.

yosser
The ZX-Spectrum manual came with a detailed explanation of this issue. I can remember thinking hard about it (at about age 10 or 11) and going “oh, that makes sense”. It was a very good manual…
Donal Fellows
+1 for 27.99998 years :-)
Péter Török
A: 

dont stops beacuose 0.2 i not precisely rapresented in two's complement soo your loop never exec 0.0==0.0 test

Maverick-F14
A: 

As others have said, this is just a fundamental problem that you get when doing floating-point arithmetic to any base. It just happens that base-2 is the most common one in computers (because it admits efficient hardware implementation).

The best fix, if possible, is to switch to using some kind of quotient representation of the number for your looping, making the floating-point value be derived from that. OK, that sounds overblown! For your specific case, I'd write it as:

int dTimes10 = 20;
double d;
while(dTimes10 != 0) {
   dTimes10 -= 2;
   d = dTimes10 / 10.0;
}

Here, we're really working with fractions [20/10, 18/10, 16/10, ..., 2/10, 0/10] where the iteration is done with integers (i.e., easy to get correct) in the numerator with a fixed denominator, before converting to floating-point. If you can rewrite your real iterations to work like this, you'll have great success (and they're really not much more expensive than what you were doing before anyway, which is a great trade-off to get correctness).

If you can't do this, you need use equal-within-epsilon as your comparison. Approximately, that's replacing d != target with abs(d - target) < ε, where ε (epsilon) selection can sometimes be awkward. Basically, the right value of ε depends on a bunch of factors, but it's probably best selected as 0.001 for the example iteration given the scale of the step value (i.e., it's half a percent of the magnitude of the step, so anything within that is going to be error instead of informative).

Donal Fellows