Search code examples
c++boostdoubleprecisionboost-lexicalcast

boost lexical cast double to string giving invalid results


I am trying this:

std::cout << boost::lexical_cast<std::string>(0.0009) << std::endl;

and expecting the output to be:

0.0009

But the output is:

0.00089999999999999998

g++ version: 5.4.0, Boost version: 1.66

What can I do to make it print what it's been given.


Solution

  • You can in fact override the default precision:

    Live On Coliru

    #include <boost/lexical_cast.hpp>
    
    #ifdef BOOST_LCAST_NO_COMPILE_TIME_PRECISION
    #    error unsupported
    #endif
    
    template <> struct boost::detail::lcast_precision<double> : std::integral_constant<unsigned, 5> { };
    
    #include <string>
    #include <iostream>
    
    int main() {
        std::cout << boost::lexical_cast<std::string>(0.0009) << std::endl;
    }
    

    Prints

    0.0009
    

    However, this is both not supported (detail::) and not flexible (all doubles will come out this way now).

    The Real Problem

    The problem is loss of accuracy converting from the decimal representation to the binary representation. Instead, use a decimal float representation:

    Live On Coliru

    #include <boost/lexical_cast.hpp>
    #include <boost/multiprecision/cpp_dec_float.hpp>
    #include <string>
    #include <iostream>
    
    using Double = boost::multiprecision::cpp_dec_float_50;
    
    int main() {
        Double x("0.009"),
               y = x*2,
               z = x/77;
    
        for (Double v : { x, y, z }) {
            std::cout << boost::lexical_cast<std::string>(v) << "\n";
            std::cout << v << "\n";
        }
    }
    

    Prints

    0.009
    0.009
    0.018
    0.018
    0.000116883
    0.000116883