121

How do you convert a float to a string in C++ while specifying the precision & number of decimal digits?

For example: 3.14159265359 -> "3.14"

Shannon Matthews
  • 9,649
  • 7
  • 44
  • 75
  • why not creating a temp float with the specified precision you want and then convert it to string? – Gilad Mar 22 '15 at 22:32
  • @Gilad The 'temp float' doesn't have a 'specified precision' and there are cases when such an approach will break down. This question is basically like asking "what is the equivalent of the '%.2f' format"? – user2864740 Mar 22 '15 at 22:33
  • 1
    See http://stackoverflow.com/questions/14432043/c-float-formatting if this is just need for IO. – user2864740 Mar 22 '15 at 22:36

7 Answers7

177

A typical way would be to use stringstream:

#include <iomanip>
#include <sstream>

double pi = 3.14159265359;
std::stringstream stream;
stream << std::fixed << std::setprecision(2) << pi;
std::string s = stream.str();

See fixed

Use fixed floating-point notation

Sets the floatfield format flag for the str stream to fixed.

When floatfield is set to fixed, floating-point values are written using fixed-point notation: the value is represented with exactly as many digits in the decimal part as specified by the precision field (precision) and with no exponent part.

and setprecision.


For conversions of technical purpose, like storing data in XML or JSON file, C++17 defines to_chars family of functions.

Assuming a compliant compiler (which we lack at the time of writing), something like this can be considered:

#include <array>
#include <charconv>

double pi = 3.14159265359;
std::array<char, 128> buffer;
auto [ptr, ec] = std::to_chars(buffer.data(), buffer.data() + buffer.size(), pi,
                               std::chars_format::fixed, 2);
if (ec == std::errc{}) {
    std::string s(buffer.data(), ptr);
    // ....
}
else {
    // error handling
}
AlexD
  • 32,156
  • 3
  • 71
  • 65
34

The customary method for doing this sort of thing is to "print to string". In C++ that means using std::stringstream something like:

std::stringstream ss;
ss << std::fixed << std::setprecision(2) << number;
std::string mystring = ss.str();
Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
32

You can use C++20 std::format:

#include <format>

int main() {
  std::string s = std::format("{:.2f}", 3.14159265359); // s == "3.14"
}

or the fmt::format function from the {fmt} library, std::format is based on (godbolt):

#include <fmt/core.h>

int main() {
  std::string s = fmt::format("{:.2f}", 3.14159265359); // s == "3.14"
}

where 2 is a precision.

It is not only shorter than using iostreams or sprintf but also significantly faster and is not affected by the locale.

Disclaimer: I'm the author of {fmt} and C++20 std::format.

vitaut
  • 49,672
  • 25
  • 199
  • 336
17

Another option is snprintf:

double pi = 3.1415926;

std::string s(16, '\0');
auto written = std::snprintf(&s[0], s.size(), "%.2f", pi);
s.resize(written);

Demo. Error handling should be added, i.e. checking for written < 0.

Columbo
  • 60,038
  • 8
  • 155
  • 203
  • Why would `snprintf` be better in this case? Please explain... It certainly won't be faster, produce less code [I have written a printf implementation, it's fairly compact at just under 2000 lines of code, but with macro expansions gets to around 2500 lines] – Mats Petersson Mar 22 '15 at 22:55
  • 1
    @MatsPetersson I strongly believe that it will be faster. That's the reason I made this answer in the first place. – Columbo Mar 22 '15 at 22:59
  • @MatsPetersson I have performed measurements (GCC 4.8.1, -O2) that indicate that snprintf is indeed taking less time, by factor 17/22. I will upload the code shortly. – Columbo Mar 22 '15 at 23:16
  • @MatsPetersson My benchmark is probably flawed, but the stringstream version takes twice as long as the snprintf version at 1000000 iterations (Clang 3.7.0, libstdc++, -O2). –  Mar 22 '15 at 23:19
  • I did some measurements too - and it would seem (at least on Linux) that both stringstream and snprintf uses the same underlying `__printf_fp` functionality - it is rather strange that it takes so much longer in `stringstream`, as it really shouldn't have to. – Mats Petersson Mar 22 '15 at 23:23
  • Still think that it's more "good C++" to use `stringstream`. If you want to optimise the output of LOTS of numbers, then there are most likely better things to do [such as writing a dedicated "floating point to string" function - although it is not entirely trivial if the numbers have a very large range] – Mats Petersson Mar 22 '15 at 23:37
  • Why sholud you write a dedicated function when there is already one in the standard library? (hint: it's in the first line of this asnwer) – edc65 Mar 23 '15 at 07:30
  • I said "if you need to optimise to something better than stringstream". – Mats Petersson Mar 24 '15 at 22:18
  • 1
    Remember it is safe to write directly to buffer owned by std::string only starting with C++11 standard. – George Keeper Mar 10 '19 at 19:36
17

Here a solution using only std. However, note that this only rounds down.

    float number = 3.14159;
    std::string num_text = std::to_string(number);
    std::string rounded = num_text.substr(0, num_text.find(".")+3);

For rounded it yields:

3.14

The code converts the whole float to string, but cuts all characters 2 chars after the "."

Duelist
  • 1,562
  • 1
  • 9
  • 24
Sebastian R.
  • 412
  • 4
  • 10
  • 2
    Only thing is that this way you are not actually rounding the number. – ChrCury78 Dec 19 '19 at 22:28
  • 1
    @ChrCury78 as stated in my answer this only rounds down. If you want a normal rounding simply add 5 to the diget following your value. For example `number + 0.005` in my case. – Sebastian R. Jan 09 '20 at 14:36
4

Here I am providing a negative example where your want to avoid when converting floating number to strings.

float num=99.463;
float tmp1=round(num*1000);
float tmp2=tmp1/1000;
cout << tmp1 << " " << tmp2 << " " << to_string(tmp2) << endl;

You get

99463 99.463 99.462997

Note: the num variable can be any value close to 99.463, you will get the same print out. The point is to avoid the convenient c++11 "to_string" function. It took me a while to get out this trap. The best way is the stringstream and sprintf methods (C language). C++11 or newer should provided a second parameter as the number of digits after the floating point to show. Right now the default is 6. I am positing this so that others won't wast time on this subject.

I wrote my first version, please let me know if you find any bug that needs to be fixed. You can control the exact behavior with the iomanipulator. My function is for showing the number of digits after the decimal point.

string ftos(float f, int nd) {
   ostringstream ostr;
   int tens = stoi("1" + string(nd, '0'));
   ostr << round(f*tens)/tens;
   return ostr.str();
}
Kemin Zhou
  • 6,264
  • 2
  • 48
  • 56
1

You can use fmt library.

3.14159265359 -> "3.14"

#include <fmt/format.h>
#include <string>

double pi = 3.14159265359;
std::string st = fmt::format("pi = {:.2f}", pi);
drNoob13
  • 11
  • 3
  • there are some answers that rely on standard libraries. So it is better to provide some context about the library you are suggesting and why it is preferable to the standard libraries – esam Apr 18 '23 at 02:51
  • 1
    Fair enough. The advantage of fmt is that it provides great flexibility of the C-like `printf` family functions while retaining the type-safe of `std::cout` while getting a huge speed boost compared to the latter. I'd like to refer reader to the documentation of [fmt](https://fmt.dev/latest/index.html) to learn more about it. For those who have access to C++20 library, you can use `std::print` which uses `fmt` as the reference implementation. – drNoob13 Apr 22 '23 at 20:37