81

Today I tried out some new functions of the C++11 STL and encountered std::to_string.

Lovely, lovely set of functions. Creating a stringstream object for just one double-to-string conversion always seemed overkill to me, so I'm glad we can now do something like this:

std::cout << std::to_string(0.33) << std::endl;

The result?

0.330000

I'm not entirely content with that. Is there a way to tell std::to_string to leave out the trailing zeros? I searched the internet, but as far as I can see the function takes only one argument (the value to be converted). Back in 'the old days' with stringstreams, you could set the width of the stream, but I'd rather not convert back.

Anyone encountered this problem before/has a solution? Some StackOverflow searches yielded nothing.

(A C++11 STL reference: http://en.cppreference.com/w/cpp/string/basic_string/to_string)

David G
  • 94,763
  • 41
  • 167
  • 253
Stijn Frishert
  • 1,543
  • 1
  • 11
  • 32
  • @chris: not after the call to `std::to_string`. It returns a string, not a number, and its formatting seems hard to modify. – rubenvb Dec 03 '12 at 15:37
  • @rubenvb, Shoot, I'm not thinking properly, am I... – chris Dec 03 '12 at 15:39
  • 3
    "Lovely, lovely set of functions. Creating a stringstream object for just one double-to-string conversion always seemed overkill to me". And then you see why it isn't really overkill -- it's streams that contain all the infrastructure for specifying the format. To get what you want, `to_string` would have to duplicate all that in an equivalent API. Which might not be a terrible idea, but it's not the idea that the committee went with. The reason `to_string` is lovely is that it has no options ;-) – Steve Jessop Dec 03 '12 at 15:59
  • 1
    @Steve: Yeah, I can see where they come from. I would just rather have to_string(double/float) not append any trailing zeros on default, and having you use stringstreams if you needed them. But that's getting into matters of preference. – Stijn Frishert Dec 03 '12 at 16:09
  • 1
    @Frishert: Yeah. I view these functions as quick-and-dirty outputters, for logging or outputs that are intended to be consumed by machines. For pretty formatting you usually have to take it into your own hands, unfortunately. – GManNickG Dec 03 '12 at 22:52

11 Answers11

75

If all you want to do is remove trailing zeros, well, that's easy.

std::string str = std::to_string (f);
str.erase ( str.find_last_not_of('0') + 1, std::string::npos );
str.erase ( str.find_last_not_of('.') + 1, std::string::npos );
BullyWiiPlaza
  • 17,329
  • 10
  • 113
  • 185
Marshall Clow
  • 15,972
  • 2
  • 29
  • 45
  • 12
    Works nearly perfect, except for the corner case where the float/double is an integral number. The result is e.g. `440.`, while some may prefer `440.0` if they're going for pretty output anyway. Nothing that a simple check of the last character won't solve, though. – Stijn Frishert Dec 05 '12 at 07:05
  • 11
    correct, this is why I adjusted Marshalls code: `namespace util { template std::string to_string(const T& t) { std::string str{std::to_string (t)}; int offset{1}; if (str.find_last_not_of('0') == str.find('.')) { offset = 0; } str.erase(str.find_last_not_of('0') + offset, std::string::npos); return str; } }` this removes an unnecessary dot also. – antibus Jan 27 '17 at 10:41
  • 6
    Repeating the last erase with a `.` is sufficient to remove trailing periods `str.erase ( str.find_last_not_of('0') + 1, std::string::npos );` `str.erase ( str.find_last_not_of('.') + 1, std::string::npos );` – Zachary Canann Jun 19 '18 at 02:19
  • yup, what @ZacharyCanann said! I ended up with a lot of numbers with "." at the end :) – thang Aug 13 '19 at 22:09
43

The C++11 Standard explicitely says (21.5/7):

Returns: Each function returns a string object holding the character representation of the value of its argument that would be generated by calling sprintf(buf, fmt, val) with a format specifier of "%d", "%u", "%ld", "%lu", "%lld", "%llu", "%f", "%f", or "%Lf", respectively, where buf designates an internal character buffer of sufficient size

for the functions declared in this order:

string to_string(int val);
string to_string(unsigned val);
string to_string(long val);
string to_string(unsigned long val);
string to_string(long long val);
string to_string(unsigned long long val);
string to_string(float val);
string to_string(double val);
string to_string(long double val);

Thus, you cannot control the formatting of the resulting string.

rubenvb
  • 74,642
  • 33
  • 187
  • 332
22

To leave out the trailing zeros:

std::ostringstream oss;
oss << std::setprecision(8) << std::noshowpoint << double_value;
std::string str = oss.str();

Note: #include <sstream> and #include <iomanip>

Ardent Coder
  • 3,777
  • 9
  • 27
  • 53
16

std::to_string gives you no control over the format; you get the same result as sprintf with the appropriate format specifier for the type ("%f" in this case).

If you need more flexibility, then you will need a more flexible formatter - such as std::stringstream.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
4

With C++20 we can finally embrace the power of std::format! Let's hope GCC and Clang will catch up to MSVC and implement it soon.

std::cout << std::format("{}\n", 0.33);
303
  • 2,417
  • 1
  • 11
  • 25
2

std::to_string(double) is defined by the standard to just return the same sequence of characters that would be generated by sprintf(buf, "%f", value). No more, no less, especially no way to tweak the format specifier. So no, there is nothing you can do.

Arne Mertz
  • 24,171
  • 3
  • 51
  • 90
1

With boost::to_string you cannot control the format either but it will output something closer to what you see in the screen. Same with std::lexical_cast<std::string>.

For a function-like operation with format control, use str(boost::format("...format...")% 0.33).

What's the difference between std::to_string, boost::to_string, and boost::lexical_cast<std::string>?

alfC
  • 14,261
  • 4
  • 67
  • 118
0

A varied solution to the problem since to_string doesn't work. "The Magic Formula" - my CS2400 teacher

std::cout.setf(ios::fixed);
std::cout.setf(ios::showpoint);
std::cout.precision(2);

const double x = 0.33, y = 42.3748;
std::cout << "$" << x << std::endl;
std::cout << "$" << y << std::endl;

Outputs:

$0.33
$42.37

any following output you do with decimal numbers will be set as so.

you can always change the setf and precision as you see fit.

StackAttack
  • 1,139
  • 1
  • 9
  • 15
0

Create a custom convert function, remove the tailing zeros if necessary.

//! 2018.05.14 13:19:20 CST
#include <string>
#include <sstream>
#include <iostream>
using namespace std;

//! Create a custom convert function, remove the tailing zeros if necessary.  
template<typename T>
std::string tostring(const T &n) {
    std::ostringstream oss;
    oss << n;
    string s =  oss.str();
    int dotpos = s.find_first_of('.');
    if(dotpos!=std::string::npos){
        int ipos = s.size()-1;
        while(s[ipos]=='0' && ipos>dotpos){
            --ipos;
        }
        s.erase ( ipos + 1, std::string::npos );
    }
    return s;
}

int main(){
    std::cout<< tostring(1230)<<endl;
    std::cout<< tostring(12.30)<<endl;
}

The input numbers :

1230
12.30

Compile with -std=c++11, then the result:

1230
12.3
Kinght 金
  • 17,681
  • 4
  • 60
  • 74
0

Write a generic helper function, such as below, which you can repurpose for rounding needs within your C++ project.

inline static const string roundDouble(const double input, const int decimal_places)
{
    ostringstream str;
    str << fixed << setprecision(decimal_places);
    str << input;
    return str.str();
}
Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
tleat
  • 1
0

Small code segment to remove trailing zero's - No streams or new Functions

double my_double = 50,003; // -> 50.003000
std::string sd = std::to_string(my_double);
for (int i = sd.size() - 1; i >= 1; i--) {
    if (sd.at(i) == '0') {
        sd.pop_back(); // Remove if last digit is '0'.
    } else if (sd.at(i) == '.') {
        sd.pop_back(); // Remove dot.
        break; // Break after '.' is removed.
    } else {
        break; // Or break before a digit is removed.
    }
}
// 50.003000 = 50.003
// 50.000000 = 50
Booniefc
  • 11
  • 1