0

I have a loop to loop a floating number between given min and max range as follow

#include <iostream>
using namespace std;

void main(void)
{
  for (double x=0.012; x<=0.013; x+=0.001) 
  {
    cout << x << endl;
  }
}

It is pretty simple code but as I know in computer language, we need to compare two floating numbers with EPS considered. Hence, above code doesn't work (we expect it to loop two times from 0.012 to 0.013 but it only loop once). So I manually add an EPS to the upper limit.

#include <iostream>
using namespace std;
#define EPS 0.0000001

void main(void)
{
  for (double x=0.012; x<=0.013+EPS; x+=0.001) 
  {
    cout << x << endl;
  }
}

and it works now. But it looks ugly to do that manually since EPS should really depends on machine. I am porting my code from matlab to C++ and I don't have problem in matlab since there is eps command. But is there anything like that in C/C++?

user1285419
  • 2,183
  • 7
  • 48
  • 70
  • 1
    No, there's nothing built in. Floating point tolerance and error handling is dependent on the application. – Barmar Oct 06 '13 at 02:58
  • 7
    Instead of iterating over floating point values, iterate with integers and then divide by 1000.0 to get fractions. – Barmar Oct 06 '13 at 02:59
  • In support of @Barmar's comment. Cumulative floating point addition can accumulate rounding error. EPS might need to be bigger for bigger iteration counts. Meanwhile, you have an opinion on how many iterations you want, in integer terms. – Patricia Shanahan Oct 06 '13 at 04:55

1 Answers1

3

Fudging the comparison is the wrong technique to use. Even if you get the comparison “right”, a floating-point loop counter will accumulate error from iteration to iteration.

You can eliminate accumulation of error by using exact arithmetic for the loop counter. It may still have floating-point type, but you use exactly representable values, such as:

for (double i = 12; i <= 13; ++i)

Then, inside the loop, you scale the counter as desired:

for (double i = 12; i <= 13; ++i)
{
    double x = i / 1000.;
     …
}

Obviously, there is not much error accumulating in a loop with two iterations. However, I expect your values are just one example, and there may be longer loops in practice. With this technique, the only error in x is in the scaling operation, so just one error in each iteration instead of one per iteration.

Note that dividing by 1000 is more accurate than scaling by .001. Division by 1000 has only one error (in the division). However, since .001 is not exactly representable in binary floating point, multiplying by it has two errors (in the conversion of .001 to floating point and in the multiplication). On the other hand, division is typically a very slow operation, so you might choose the multiplication.

Finally, although this technique guarantees the desired number of iterations, the scaled value might be slight outside the ideal target interval in the first or the last iteration, due to the rounding error in the scaling. If this matters to your application, you must adjust the value in these iterations.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • is there a reason whey C++11's 'less'/'isgreater' is not used? would they also result in unexpected behavior? – Hossein Oct 25 '20 at 17:52
  • 1
    @Rika: `isless` and `isgreater` are no different from `<` and `>` for non-NaN values. – Eric Postpischil Oct 25 '20 at 17:58
  • thanks a lot really? I had the expectation that their new addition to C++11, was actually something more than this! i.e. they incorporate that eps internally for comparisons – Hossein Oct 25 '20 at 18:35
  • 1
    @Rika: [There is no general solution for comparing floating-point numbers that contain errors from previous operations](https://stackoverflow.com/a/21261885/298225), and it would be bad to build an approximate comparison into a language, since it would mislead people into think there is a general solution. – Eric Postpischil Oct 25 '20 at 18:41