2015-06-08 7 views
11

Mam następujący problem: klient chce przedstawić typ double na string w optymalny sposób. musi zostać przekonwertowany na ciąg znaków i wyświetlony w formularzu (sms, tabela i inne).Jak przekonwertować "podwójny" na "ciąg" w przyjaznej dla użytkownika formie

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; 

Kod dla konwersji jest występ krytyczne, to znaczy, że nie std::stringstream + setprecision() powinny być stosowane.

Wspaniale byłoby wprowadzić precyzję params jako argument mojej funkcji toStringNew(), ale te ulepszenia mogą krytycznie wpłynąć na cały nasz system, a my planujemy wprowadzić go w następnej wersji. Ale ten problem jest teraz aktualny.
Napisałem następujący kod:

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; 
} 

To działa na 1,5 razy szybciej niż boost::spirit::karma::generate() dla double typu.

Czy możesz mi doradzić, jak zadowolić klienta?

+2

Czy próbowałeś zmierzyć, jak wolny jest 'stringstream'? Zgaduję, że jest szybszy, niż się spodziewasz. –

+1

co z 'std :: to_string'? –

+0

Próbowałem za pomocą sstream i mam następujące wyniki: 'New toString() TPS = 2257627.5000000000' ' SStream toString() TPS = 503520.9375000000' 'stary toString() TPS = 1871579.8750000000' std :: stringstream jest zbyt powoli –

Odpowiedz

0

Jeśli wydajność jest krytyczna, może użyć snprintf. To będzie lepsze niż twoje domowe rozwiązanie i myślę, że będzie lepiej. Za pomocą specyfikatora formatu * można również ustawić precyzję jako argument.

+0

'New toString()' ' TPS = 4086582.7500000000' ' SStream toString() '' TPS = 518112.9062500000' 'Sprintf toString() ' ' TPS = 1592562.3750000000' 'stary ToString()' 'TPS = 2909644.5000000000' 'snprintf' jest wolniejsze. Potrzebuje wydajności co najmniej jako "stare ToString" –

8

Spojrzałbym na C++ String Toolkit Library. Użyłem go do analizy parsowania i konwersji liczb i okazało się, że jest bardzo szybki.

#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; 
}