What is the fastest way to convert a rounded double into a string ? (no trailing zeros, no spaces)

Hi everyone,

I'm looking for the fastest C++ routine to convert this double into a string.
The number must be rounded to 2 decimal places, no trailing zeros, no spaces.

___double__________string______
145.544999999 ---> "145.54"
145.55000 -------> "145.55"
145.0050 --------> "145.01"
145.004999999 ---> "145"


Thanks!

Last edited on
None of these values are able to be represented exactly as a double.

Illustration:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>
#include <iomanip>
#include <limits>
#include <sstream>

std::size_t max_prec = std::numeric_limits<double>::max_digits10;

std::string to_string(double val, std::size_t prec)
{
    std::ostringstream os;
    os << std::fixed << std::setprecision(prec) << val;
    return os.str();
}

int main()
{   
    double values [] = { 
        145.544999999, 145.55000, 
        145.0050, 145.004999999 
    };

    for (auto val : values)
    {
        std::cout << to_string(val, max_prec) << " => ";
        std::cout << to_string(val, 2) << '\n';
    }
}
145.54499999899999807 => 145.54
145.55000000000001137 => 145.55
145.00499999999999545 => 145.00
145.00499999900000603 => 145.00
For reasonably small numbers (up to about six digits or so before the decimal point),
std::numeric_limits<double>::is_iec559 == true (it is usually true):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#include <string>
#include <cmath>
#include <sstream>
#include <iomanip>

std::string round_to_string( double dv )
{
    long long lv = std::llround( dv * 100 ) ;

    if( lv%100 == 0 ) return std::to_string( lv/100 ) ;

    else
    {
        std::ostringstream stm ;
        stm << std::fixed << std::setprecision(2) << lv / 100.0 ;
        return stm.str() ;
    }
}

int main()
{
    std::cout << std::fixed << std::setprecision(10) ;
    for( double v : { 145.544999999, 145.55000, 145.0050, 145.004999999, -14.544999999, -14.545000 } )
        std::cout << v << "  " << std::quoted( round_to_string(v) ) << '\n' ;
}

145.5449999990  "145.54"
145.5500000000  "145.55"
145.0050000000  "145.01"
145.0049999990  "145"
-14.5449999990  "-14.54"
-14.5450000000  "-14.55"

http://coliru.stacked-crooked.com/a/df238ef661282088
http://rextester.com/NAOY82099
Thanks cire & JLBorges.

cire, how do you remove your trailing zeros?

JLBorges, if I change the precision from 100 to 1000 and setprecision(3), then " if( lv%1000 == 0 ) return std::to_string( lv/1000 );" never execute and I have trailing zeros.

What should I change in order to get any precision I need (up to 8 decimal digits) without trailing zeros?

Thanks!
> if I change the precision from 100 to 1000 and setprecision(3),
> then " if( lv%1000 == 0 ) return std::to_string( lv/1000 );" never execute and I have trailing zeros.

I'm not able to reproduce that behaviour.


> What should I change in order to get any precision I need (up to 8 decimal digits) without trailing zeros?

Something like this, perhaps:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <iostream>
#include <string>
#include <cmath>
#include <sstream>
#include <iomanip>

long long ipow10( int n ) // invariant: positive n, does not overflow
{
    long long p = 1 ;
    while( n-- ) p *= 10 ;
    return p ;
}

std::string round_to_string( double dv, int prec = 3 ) // positive precision
{
    const long long multiplier = ipow10(prec) ;
    const long long lv = std::llround( dv * multiplier ) ;

    if( lv%multiplier == 0 ) return std::to_string( lv/multiplier ) ;

    else
    {
        std::ostringstream stm ;
        stm << std::fixed << std::setprecision(prec) << lv / double(multiplier) ;
        return stm.str() ;
    }
}

int main()
{
    std::cout << std::fixed << std::setprecision(10) ;

    for( int prec = 2 ; prec < 6 ; ++prec )
    {
        std::cout << "precision: " << prec << "\n------------------------\n" ;

        for( double v : { 1.4999999999, 1.0499999999, 1.0049999999, 1.0004999999, 1.0000499999, 1.0000049999,
                          1.5000000001, 1.0500000001, 1.0050000001, 1.0005000001, 1.0000500001, 1.0000050001 } )
            std::cout << std::setw(18) << v << "  " << std::quoted( round_to_string( v, prec ) ) << '\n' ;

        std::cout << '\n' ;
    }
}

precision: 2
------------------------
      1.4999999999  "1.50"
      1.0499999999  "1.05"
      1.0049999999  "1"
      1.0004999999  "1"
      1.0000499999  "1"
      1.0000049999  "1"
      1.5000000001  "1.50"
      1.0500000001  "1.05"
      1.0050000001  "1.01"
      1.0005000001  "1"
      1.0000500001  "1"
      1.0000050001  "1"

precision: 3
------------------------
      1.4999999999  "1.500"
      1.0499999999  "1.050"
      1.0049999999  "1.005"
      1.0004999999  "1"
      1.0000499999  "1"
      1.0000049999  "1"
      1.5000000001  "1.500"
      1.0500000001  "1.050"
      1.0050000001  "1.005"
      1.0005000001  "1.001"
      1.0000500001  "1"
      1.0000050001  "1"

precision: 4
------------------------
      1.4999999999  "1.5000"
      1.0499999999  "1.0500"
      1.0049999999  "1.0050"
      1.0004999999  "1.0005"
      1.0000499999  "1"
      1.0000049999  "1"
      1.5000000001  "1.5000"
      1.0500000001  "1.0500"
      1.0050000001  "1.0050"
      1.0005000001  "1.0005"
      1.0000500001  "1.0001"
      1.0000050001  "1"

precision: 5
------------------------
      1.4999999999  "1.50000"
      1.0499999999  "1.05000"
      1.0049999999  "1.00500"
      1.0004999999  "1.00050"
      1.0000499999  "1.00005"
      1.0000049999  "1"
      1.5000000001  "1.50000"
      1.0500000001  "1.05000"
      1.0050000001  "1.00500"
      1.0005000001  "1.00050"
      1.0000500001  "1.00005"
      1.0000050001  "1.00001"


http://coliru.stacked-crooked.com/a/84d849c914776e61
http://rextester.com/SOAL5961
> if I change the precision from 100 to 1000 and setprecision(3),
> then " if( lv%1000 == 0 ) return std::to_string( lv/1000 );" never execute and I have trailing zeros.

I'm not able to reproduce that behaviour.


> What should I change in order to get any precision I need (up to 8 decimal digits) without trailing zeros?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
static long long ipow10( int n ) // invariant: positive n, does not overflow
{
    long long p = 1 ;
    while( n-- ) p *= 10 ;
    return p ;
}

// use rounding facilities provided by the stream library
std::string round_to_string_1( double dv, int prec = 3 ) // positive precision
{
    std::ostringstream stm ;
    stm << std::fixed << std::setprecision(prec) << dv ;
    std::string result = stm.str() ;

    while( result.back() == '0' ) result.pop_back() ;
    if( result.back() == '.' ) result.pop_back() ;

    return result ;
}

// use rounding facilities provided by the math library
std::string round_to_string_2( double dv, int prec = 3 ) // positive precision
{
    const long long multiplier = ipow10(prec) ;
    const long long lv = std::llround( dv * multiplier ) ;

    if( lv%multiplier == 0 ) return std::to_string( lv/multiplier ) ;

    else
    {
        std::ostringstream stm ;
        stm << std::fixed << std::setprecision(prec) << lv / double(multiplier) ;
        std::string result = stm.str() ;
        while( result.back() == '0' ) result.pop_back() ;
        return result ;
    }
}
thanks! perfect as always!
Topic archived. No new replies allowed.