Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 854
AbbasFaisal Avatar asked Oct 19 '25 14:10

AbbasFaisal


2 Answers

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
like image 95
sehe Avatar answered Oct 21 '25 02:10

sehe


boost::lexical_cast doesn't allow you to specify the precision when converting a floating point number into its string representation. From the documentation

For more involved conversions, such as where precision or formatting need tighter control than is offered by the default behavior of lexical_cast, the conventional std::stringstream approach is recommended.

So you could use stringstream

double d = 0.0009;
std::ostringstream ss;
ss << std::setprecision(4) << d;
std::cout << ss.str() << '\n';

Or another option is to use the boost::format library.

std::string s = (boost::format("%1$.4f") % d).str();
std::cout << s << '\n';

Both will print 0.0009.

like image 42
Praetorian Avatar answered Oct 21 '25 04:10

Praetorian