AlQuemist
AlQuemist

Reputation: 1304

Strangeness in Conversion from String to Double in C++

I have to read some data (with double precision) from a text file. Sometimes, I get strange results when converting from string to double with atof. The following is a code snippet which demonstrates the problem -- note that I am using a GNU C++ 4.8.1 compiler. The code simply prints out the numbers which are between -0.1 and 0 taking steps of size 0.01; so only, 9 numbers are expected.

    #include <iostream>
    #include <cstdlib>

    int main() {
    int y;
    double a, b = 0.0, x = 1e-2, z;

    double a_string = atof("-0.1");
    double a_inline = -0.1;

    std::cout << "Inline:: ";
    a = a_inline;
    for (y = 1 ; (z = a + y * x) < b; ++y){
    std::cout << y << ": " << z << " | ";
    }

    std::cout << "\nString:: ";
    a = a_string;
    for (y = 1 ; (z = a + y * x) < b; ++y){
    std::cout << y << ": " << z << " | ";
    }

    return 0;
    }

The result is

    Inline:: 1: -0.09 | 2: -0.08 | 3: -0.07 | 4: -0.06 | 5: -0.05 | 6: -0.04 | 7: -0.03 | 8: -0.02 | 9: -0.01 | 
    String:: 1: -0.09 | 2: -0.08 | 3: -0.07 | 4: -0.06 | 5: -0.05 | 6: -0.04 | 7: -0.03 | 8: -0.02 | 9: -0.01 | 10: -3.46945e-18 | 

Note that the for loop based on the string assignment (a_string) runs one time more than the loop based on the inline assignment (a_inline) -- notice the last number -3.46945e-18. Having read other relevant posts on string-to-double conversion, still I could not figure out why this happens.

Upvotes: 3

Views: 365

Answers (2)

Tony Delroy
Tony Delroy

Reputation: 106076

Your code amounts to...

std::atof("-0.1") - 10 * 1e-2

...not equaling exactly 0. That's life with real number representations, which are only approximations capable of encoding a finite number of points spread across their range, and some of the functions generating/handling them may introduce errors in the last digit or two even for numbers than happen to be perfectly representable. In your case, -0.1 can not be perfectly represented. To get a feel for this, type "-0.1" into the "Decimal Representation" text box in this online IEEE 754 Converter, and you'll see that the binary representation possible in 32-bit floats is only approximate... in this case that's equally true of what's possible in 64-bit doubles... you just have a better approximation.

The usual advice is to go read What Every Computer Scientist Should Know About Floating Point Values

What's mathematically "x - x" may not be 0 when the calculation of each x has been done differently or at different times, and exactly how it pans out can vary with optimisation level, surrounding code, hardware, compiler etc.. It's a bit of an art form designing code that handles these issues gracefully. In your case, if you want 9 iterations of the loop then count to 9, if you want some tolerance in the comparison then write something that allows a small margin for error (an "epsilon" value) in the comparison. You can also use functions that bump floating point numbers to the next representable value one bit at a time, which avoids issues with scaling an epsilon value so it corresponds to the last significant digit or two with the specific exponents involved, but that's a bit of a game too as it's hard to know how many representation and rounding errors accumulated in the generation of a value.

Upvotes: 2

Peter - Reinstate Monica
Peter - Reinstate Monica

Reputation: 16017

Binary floats are usually not exact representations of decimal fractions. Direct comparisons of different computed results or of computed results with literals are bound to fail unintuitively. You must work with intervals when comparing. The problem has little to do with atof as such.

Upvotes: 4

Related Questions