For-loop in C++ using double breaking out one step early, boundary value not reached

让人想犯罪 __ 提交于 2019-11-26 21:03:43

问题


I have a simple C++ program compiled using gcc 4.2.4 on 32-bit Ubuntu 8.04. It has a for-loop in which a double variable is incremented from zero to one with a certain step size. When the step size is 0.1, the behavior is what I expected. But when the step size is '0.05', the loop exits after 0.95. Can anyone tell me why this is happening? The output follows the source code below.

#include <iostream>

using namespace std;

int main()
{
    double rangeMin = 0.0;
    double rangeMax = 1.0;
    double stepSize = 0.1;

    for (double index = rangeMin; index <= rangeMax; index+= stepSize)
    {
        cout << index << endl;
    }
    cout << endl; 

    stepSize = 0.05;
    for (double index = rangeMin; index <= rangeMax; index+= stepSize)
    {
        cout << index << endl;
    }

    return 0;
}

OUTPUT

sarva@savija-dev:~/code/scratch$ ./a.out 
0
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.8
0.9
1

0
0.05
0.1
0.15
0.2
0.25
0.3
0.35
0.4
0.45
0.5
0.55
0.6
0.65
0.7
0.75
0.8
0.85
0.9
0.95
sarva@savija-dev:~/code/scratch$

回答1:


When using floating point values not every value is exactly representable, 0.95+0.05 > 1 because 0.95 is not exactly representable by a double value.

See what Wikipedia has to say about floating point accuracy.

If you look at the IEEE floating point converter you'll see that the value of 0.95 in 64 bit floating point (double) is 0-01111111110-1110011001100110011001100110011001100110011001100110 by entering this in a floating point calculator you get the value is 0.95000016 and adding 0.05 to that takes you over the 1.0 mark.

This is why you should never use floating points in loops (or more generally compare the result of floating point calculation to an exact value).




回答2:


Generally, when you compare doubles, simple comparison is not good enough, and you should compare them "up to a precision". i.e. :

if ( fabs(double1-double2) < 0.0000001 ) {
  do-something
}

The problem occurs due to the representation of double variables.




回答3:


As mentioned by others, this is a well known issue due to the inexact representation of certain decimal numbers in memory. I would highly recommend reading What Every Computer Scientist Should Know About Floating-Point Arithmetic and IEEE floating-point representations of real numbers.




回答4:


You should not use == or <= for doubles due to its internal representation. On last step you'll get 0.95000000000000029. Instead you could use the following code:

stepSize = 0.05;
// stepSize/2 looks like a good delta for most cases
for (double index = rangeMin; index < rangeMax+stepSize/2; index+= stepSize)
{
    cout << index << endl;
}

For more details read What Every Computer Scientist Should Know About Floating-Point Arithmetic.




回答5:


Most exact decimals do not have an exact finite representation in floating point arithmetic.

You need to read Goldberg's What Every Computer Scientist Should Know About Floating-Point Arithmetic.




回答6:


Probably the last index value would be like 1.00000001.




回答7:


As others have said, not every real number is exactly representable as a floating point value, so you can expect a small, "random" rounding error in floating-point calculations. It is similar to what happens with normal decimal digits: 1/3 isn't exactly representable using three decimal digits (0.33), so (1/3)*3 would become 0.99 and not exactly 1.

It is possible to use some sort of "precision" in your comparisons, but I would recommend avoiding floating-point numbers for loops, and instead use integers.

For example, your loop

stepSize = 0.05;
for (double index = rangeMin; index <= rangeMax; index+= stepSize)
{
    cout << index << endl;
}

could be replaced by something along the lines of

stepSize = 0.05;
for (int index = 0; index < 21; ++index)
{
    double value = rangeMin + index * stepSize;
    cout << value << endl;
}



回答8:


This is due to the inexact representation of decimal fractions by floating-point numbers. Your step size isn't actually 0.1 or 0.05, it's some other value that's very close. The slight error accumulates as you go through the loop.

To solve this problem, you have to avoid comparing floating-point numbers for equality.




回答9:


See this output: (floating point accuarcy)

#include <iostream>
#include <iomanip>
using namespace std;
int main(){
    double rangeMin = 0.0;
    double rangeMax = 1.0;
    double stepSize = 0.1;
    double index;
    for (index = rangeMin;  index <= rangeMax; index+=stepSize)
        {
               cout << fixed << setprecision(16) <<  index << endl;
         }
  cout << endl;
  stepSize = 0.05;
  for (index = rangeMin; index<= rangeMax; index+= stepSize)
     {
         cout << index << endl;
             }

   cout << "\n" << setprecision(16) << index << " "  << rangeMax;
   if(index==rangeMax)
      cout << "\nEQ";
   else
     cout << "\nNot EQ";
     return 0;
}

0.0000000000000000
0.1000000000000000
0.2000000000000000
0.3000000000000000
0.4000000000000000
0.5000000000000000
0.6000000000000000
0.7000000000000000
0.7999999999999999
0.8999999999999999
0.9999999999999999

0.0000000000000000
0.0500000000000000
0.1000000000000000
0.1500000000000000
0.2000000000000000
0.2500000000000000
0.3000000000000000
0.3500000000000000
0.4000000000000000
0.4500000000000000
0.4999999999999999
0.5499999999999999
0.6000000000000000
0.6500000000000000
0.7000000000000001
0.7500000000000001
0.8000000000000002
0.8500000000000002
0.9000000000000002
0.9500000000000003

1.0000000000000002 1.0000000000000000
Not EQ



回答10:


As the previous answers , it is not accurate to use non-integers in for-loops , So I suggest to do as the following example so you keep the accuracy of integers and you can get the decimals you want:

#include<iostream>
#include<cmath>
#include<iomanip>
using namespace std; 

int main()
{
for (double y = 1; y!=10; y += 1)
    cout << static_cast<double>(y/10) << endl; 



}


来源:https://stackoverflow.com/questions/1286394/for-loop-in-c-using-double-breaking-out-one-step-early-boundary-value-not-rea

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