Why does this loop never end? [duplicate]

纵饮孤独 提交于 2019-12-10 12:29:10

问题


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;
}

回答1:


Floating point calculations are not perfectly precise. You will get a representation error because 0.2 doesn't have an exact representation as a binary floating point number so the value doesn't become exactly 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.




回答2:


(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.




回答3:


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.




回答4:


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.




回答5:


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!




回答6:


f is uninitialised ;)

If you mean:

double f = 2.0;

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




回答7:


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){

}



回答8:


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).




回答9:


It doesn't stop because 0.2 i not precisely represented in two's complement so your loop never executes the 0.0==0.0 test



来源:https://stackoverflow.com/questions/3351148/why-does-this-loop-never-end

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!