Sergei_Ivanov
Sergei_Ivanov

Reputation: 237

How to convert 'double' to 'string' in user-friendly form

i have the following problem: customer want to present double type to a string by an optimal way. it need to be converted to string and showed on a form(sms, table and other).

100000000.949999 needs to be converted in 100000000.950;
10.000000001 -> 10.0;
100000000000000.19292 -> 1e+14;
1000.123456789 -> 1000.123;
0.00001 -> 0.0;

The code for converting is a performance critical, it means that no std::stringstream + setprecision() should be used.

It will be great to implement params 'precision' as argument of my toStringNew() function, but this improvements may critically affects all our system, and we are planning implement it in the next release. But this problem is actual now.
I wrote the following code:

inline bool toStringNew(std::string& str, double const& value)
{
    static const char *zero_double_str = "0.0";
    static const double zero_double_limit = 0.001;
    static const int max_double_prec_symbol = 13;
    static const int max_fract_num = 3;
    static const int max_fract_mul = pow(10, max_fract_num);
    str.clear();
    //get digits of integer part
    double fabs_value = fabs(value);
    int64_t len = log10(fabs_value);
    //Round 2 zero
    if(len <= 0) //it means that only fraction part is present
    {
        if(fabs_value < zero_double_limit)
        {
            str = zero_double_str;
            return true;
        }
        //use default
        return boost::spirit::karma::generate(std::back_inserter(str), value);
    }
    else if(len > max_double_prec_symbol)   //default
    {
        return boost::spirit::karma::generate(std::back_inserter(str), value);
    }
    //cast to integer
    int64_t i = static_cast<int64_t>(value);
    //cast fract to integer
    int64_t fract_i = static_cast<int64_t>(round((value - i)* max_fract_mul));
    //reserve string memory
    size_t str_len = len + 1 +  max_fract_num + (value > 0 ? 0 : 1) + 1; 
    str.reserve(str_len);
    //convert integer
    boost::spirit::karma::generate(std::back_inserter(str), i);
    str+='.';
    //convert fract
    if(fract_i > 0)
    {
        str+='.';
        int64_t fract_i_len = log10(fract_i);
        //fill zero before: 0.001 -> 1 -> 001
        while(++fract_i_len < max_fract_num)
        {
            str += '0';
        }
        //remove zero after: 010 -> 01
        while(!(fract_i % 10))
        {
            fract_i = fract_i / 10;
        }
        boost::spirit::karma::generate(std::back_inserter(str), fract_i);
    }
    boost::spirit::karma::generate(std::back_inserter(str), fract_i);
    return true;
}

This works at 1,5 times faster than boost::spirit::karma::generate() for double type.

Can you give me some advices how to satisfy my customer?

Upvotes: 12

Views: 426

Answers (2)

DannyK
DannyK

Reputation: 1552

I would look at the C++ String Toolkit Library. I have used it for parsing and number conversion and it has shown to be very fast.

#include <cmath>
#include <strtk.hpp>
#include <iostream>
using namespace std;

int main ( int argc, char **argv ) 
{
   double pi = M_PI;
   std::cout << strtk::type_to_string<double>( pi ) << std::endl;
   return 0;
}

Upvotes: 6

Ivaylo Strandjev
Ivaylo Strandjev

Reputation: 70929

If performance is critical maybe use snprintf. It will be better than your home grown solution and also I think it will perform better. Using the * format specifier you can also make the precision an argument.

Upvotes: 0

Related Questions