1

When I was programming a little ODE-solver for which I needed a time array, I ended up with the following really strange behavior. Following code should clearly reproduce the problem.

#include <iostream>
using namespace std;

int main() {
    double t = 0.0;
    for (t = 0.0; t <= 1.00; t += 0.01) {
        cout << t << " ";
    }
    cout << endl;

    cout << "t = "<<  t << " and t <= 1.00? " << (t <= 1.00) << endl;
    double c = 1.00;
    cout << "c = "<<  c << " and c <= 1.00? " << (c <= 1.00) << endl;
    cout << "t == 1.00? " << (t == 1.00) << " and c == 1.00? " << (c == 1.00) << endl;
    return 0;
}

This gives the following output:

0 0.01 0.02 0.03 0.04 ... 0.97 0.98 0.99 
t = 1 and t <= 1.00? 0
c = 1 and c <= 1.00? 1
t == 1.00? 0 and c == 1.00? 1

My question is: why does (t <= 1.00) and (t == 1.00) returns false while t should clearly be equal to 1 and t is of the type double?

I cannot really avoid this problem cause in my real code my t-step is not hard coded etc...

Thank you on beforehand.

Edit: thank you for the answers. It's indeed the problem of the binary representations of 0.01 etc not being exact but with a certain rounding error. The t scale was really enforced by the other physical quantities in the program. Another answer/hint I got elsewhere in the meantime was to always work with a tolerance if working with floating point members is needed. In this case "t <= 1.00" could become "t < 1.00 + 0.01/2" or more general "t < 1.00 + h/2" when working with a variable precision h as was needed in my application.

Josja
  • 131
  • 7
  • @tobi303: It's supposed to yield false. The loop wouldn't stop if it was true. (As for `c <= 1.00` being true, that's because `c != t`.) – user2357112 Oct 10 '17 at 17:22
  • @user2357112 yeah I just realized my stupidity. So its is just as expected when taking into account that floats dont comare nicely – 463035818_is_not_an_ai Oct 10 '17 at 17:23
  • Try adding `cout.precision(17);` at the very start of your `main` and the problem will be made apparent (or raise a more pertinent question). You are only displaying a few decimal places which rounds off the accumulating error. – François Andrieux Oct 10 '17 at 17:24
  • why dont you discretize your steps, ie: `for (int i = 0;i< imax;i++){ t = i*dt; ...` a float as loop variable isnt the best idea – 463035818_is_not_an_ai Oct 10 '17 at 17:25

1 Answers1

2

Floating point arithmetic doesn't work as you might expect.

The culprit is your condition in the for loop:

t <= 1.00

t is double and as a consequence adding 100 times 0.01 to 0.00 is not exactly equal to 1.00 but approximately equal to it.

You can solve this way:

int t;
for (t = 0; t <= 100; t++) {
    std::cout << static_cast<double>(t)/100 << " ";
}

And then:

 std::cout << "t = "<<  static_cast<double>(t)/100 << " and t <= 1.00? " << (static_cast<double>(t)/100 <= 1) << std::endl;
Neb
  • 2,270
  • 1
  • 12
  • 22