Menu

Simple arithmetic problem with while and dbl

David
2008-09-24
2012-09-26
  • David

    David - 2008-09-24

    This compiles fine in dev CPP 4.9.9.2:

    include <cstdlib>

    include <iostream>

    using namespace std;

    int main()
    {
    double a = 0.03;
    while (a >= 0) {a -= 0.01; cout << a << endl;}

    system(&quot;PAUSE&quot;);
    return EXIT_SUCCESS;
    

    }

    And yet, instead of couting down to 0 it counts down and outputs a crazy small number where 0 should be.

    I've tried this on three computers and even using the old dev cpp 4. What am I doing wrong here? Also, oddly enough, if you change a to 0.02 it works fine, but anything above that and it breaks.

    Thanks for your help!

     
    • cpns

      cpns - 2008-09-24

      That is the nature of binary floating point representations. A double is a 64bit value, it has 2^64 finite states to represent an infinite number of real world values. So ant stored value is necessarily an approximation.

      For reasons of hardware implementation efficiency, a binary floating point representation is used, and the approximations do not necessarily exactly coincide with any particular decimal value.

      So, in this case for example, 0.01 cannot be exactly represented by a binary floating point value, it may be something like 0.0099999999999 for example. When you cumulatively decrement the variable a, you will never get to precisely zero.

      Moreover when you use sts::ostream::operator<< to display a value, it will convert the binary representation to a decimal string and apply appropriate rounding depending upon teh prevailing precision setting, so it will likley display 0.00999999999 as 0.01, so you might not see the error until it becomes large enough with respect to the display precision (which can be set by iostream manipulators).

      I just ran your code in MSVC++ and used the devugger to observe the stored value of 'a' after initialisation, and it was 0.029999999999999999 - illustrating my point.

      You can 'solve' the problem by controlling the way numbers are displayed with iomanipilators ( http://www.cplusplus.com/reference/iostream/manipulators/ ). For example:

      include <cstdlib>

      include <iostream>

      include <iomanip>

      using namespace std;

      int main()
      {
      double a = 0.03;
      while (a >= 0) {a -= 0.01; cout << setprecision(2) << fixed << a << endl;}

      system(&quot;PAUSE&quot;); 
      return EXIT_SUCCESS;
      

      }

      However in my test it rather strangley perhaps displayed the final value a -0.00, the GNU iostream library may or may not do that.

      All that said your code is still seriously flawed from a floating point perspective. It may be jist as likely that the 0.03 approximation were 0.030000000001, in which case, the 'near zero' value would be positive, and an additional iteration would occur stopping at 'near -0.01'. One solution to that problem is:

      while ( fabs(a) > 0.001 )

      You can test this as I did by initialising 'a' to 0.030000001. The fabs() function is declared in <cmath>, so you need to #include that.

      Another solution is to use decimal floating point, but it requires library or language extension support in C/C++ and is less efficient. It is normally used for financial applications where it is essential that the computed results are identical to those that would be produced had one used paper and pencil or a calculator. Decimal floating point is not more or less precise for general mathematics, it merely produces 'expected' results consistent with the rounding rules and conventions applied in 'normal' arithmetic - and stops you getting stupid values in your telephone bill!

      Clifford

       
      • David

        David - 2008-09-24

        Thanks! This is exactly what I needed.

         

Log in to post a comment.