Как преобразовать 'double' в 'string' в удобной для пользователя форме
У меня есть следующая проблема:
клиент хочет представить double
тип string
оптимальным способом. его нужно преобразовать в строку и показать на форме (смс, таблицу и т.д.).
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;
Код для преобразования является критическим для производительности, это означает, что не следует использовать std::stringstream
+ setprecision()
.
Было бы здорово реализовать точность params как аргумент моей функции toStringNew()
, но эти улучшения могут критически повлиять на всю нашу систему, и мы планируем реализовать ее в следующей версии.
Но эта проблема актуальна сейчас.
Я написал следующий код:
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;
}
Это работает в 1,5 раза быстрее, чем boost::spirit::karma::generate()
для типа double
.
Можете ли вы дать мне несколько советов, как удовлетворить моего клиента?
Ответы
Ответ 1
Я бы посмотрел на С++ String Toolkit Library. Я использовал его для разбора и преобразования чисел, и он показал, что он очень быстрый.
#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;
}
Ответ 2
Если производительность критическая, используйте snprintf. Это будет лучше, чем ваше домашнее решение, и я думаю, что он будет работать лучше. Используя спецификатор формата *
, вы также можете сделать точность аргументом.