C++11 Std::To_String(Double) - No Trailing Zeros

C++11 std::to_string(double) - No trailing zeros

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.

double to std::string with dynamic precisicion (without trailing zeros)

You can use string stream (sstring) with stream manipulators, see example below:

  std::stringstream ss1;
std::stringstream ss2;
ss1.precision(15);
ss1 << 3.14;
std::cout << ss1.str()<<' '<<("3.14" == ss1.str())<<std::endl;
ss2.precision(15);
ss2 << 3.1415926536;
std::cout << ss2.str()<<' '<<("3.1415926536" == ss2.str())<<std::endl;

Or you can use boost format. Here's a link!

  std::cout<<format("%.2f") % 3.14 <<std::endl;
std::cout<<format("%.10f") % 3.1415926536 <<std::endl;

double to string without scientific notation or trailing zeros, efficiently

Before you start, check whether significant time is spent in this function. Do this by measuring, either with a profiler or otherwise. Knowing that you call it a zillion times is all very well, but if it turns out your program still only spends 1% of its time in this function, then nothing you do here can possibly improve your program's performance by more than 1%. If that were the case the answer to your question would be "for your purposes no, this function cannot be made significantly more efficient and you are wasting your time if you try".

First thing, avoid s.substr(0, s.size()-1). This copies most of the string and it makes your function ineligible for NRVO, so I think generally you'll get a copy on return. So the first change I'd make is to replace the last line with:

if(s[s.size()-1] == '.') {
s.erase(s.end()-1);
}
return s;

But if performance is a serious concern, then here's how I'd do it. I'm not promising that this is the fastest possible, but it avoids some issues with unnecessary allocations and copying. Any approach involving stringstream is going to require a copy from the stringstream to the result, so we want a more low-level operation, snprintf.

static std::string dbl2str(double d)
{
size_t len = std::snprintf(0, 0, "%.10f", d);
std::string s(len+1, 0);
// technically non-portable, see below
std::snprintf(&s[0], len+1, "%.10f", d);
// remove nul terminator
s.pop_back();
// remove trailing zeros
s.erase(s.find_last_not_of('0') + 1, std::string::npos);
// remove trailing point
if(s.back() == '.') {
s.pop_back();
}
return s;
}

The second call to snprintf assumes that std::string uses contiguous storage. This is guaranteed in C++11. It is not guaranteed in C++03, but is true for all actively-maintained implementations of std::string known to the C++ committee. If performance really is important then I think it's reasonable to make that non-portable assumption, since writing directly into a string saves copying into a string later.

s.pop_back() is the C++11 way of saying s.erase(s.end()-1), and s.back() is s[s.size()-1]

For another possible improvement, you could get rid of the first call to snprintf and instead size your s to some value like std::numeric_limits<double>::max_exponent10 + 14 (basically, the length that -DBL_MAX needs). The trouble is that this allocates and zeros far more memory than is typically needed (322 bytes for an IEEE double). My intuition is that this will be slower than the first call to snprintf, not to mention wasteful of memory in the case where the string return value is kept hanging around for a while by the caller. But you can always test it.

Alternatively, std::max((int)std::log10(d), 0) + 14 computes a reasonably tight upper bound on the size needed, and might be quicker than snprintf can compute it exactly.

Finally, it may be that you can improve performance by changing the function interface. For example, instead of returning a new string you could perhaps append to a string passed in by the caller:

void append_dbl2str(std::string &s, double d) {
size_t len = std::snprintf(0, 0, "%.10f", d);
size_t oldsize = s.size();
s.resize(oldsize + len + 1);
// technically non-portable
std::snprintf(&s[oldsize], len+1, "%.10f", d);
// remove nul terminator
s.pop_back();
// remove trailing zeros
s.erase(s.find_last_not_of('0') + 1, std::string::npos);
// remove trailing point
if(s.back() == '.') {
s.pop_back();
}
}

Then the caller can reserve() plenty of space, call your function several times (presumably with other string appends in between), and write the resulting block of data to the file all at once, without any memory allocation other than the reserve. "Plenty" doesn't have to be the whole file, it could be one line or "paragraph" at a time, but anything that avoids a zillion memory allocations is a potential performance boost.

Remove trailing zero in C++

This is one thing that IMHO is overly complicated in C++. Anyway, you need to specify the desired format by setting properties on the output stream. For convenience a number of manipulators are defined.

In this case, you need to set fixed representation and set precision to 2 to obtain the rounding to 2 decimals after the point using the corresponding manipulators, see below (notice that setprecisioncauses rounding to the desired precision). The tricky part is then to remove trailing zeroes. As far as I know C++ does not support this out of the box, so you have to do some string manipulation.

To be able to do this, we will first "print" the value to a string and then manipulate that string before printing it:

#include <iostream>
#include <iomanip>

int main()
{
double value = 12.498;
// Print value to a string
std::stringstream ss;
ss << std::fixed << std::setprecision(2) << value;
std::string str = ss.str();
// Ensure that there is a decimal point somewhere (there should be)
if(str.find('.') != std::string::npos)
{
// Remove trailing zeroes
str = str.substr(0, str.find_last_not_of('0')+1);
// If the decimal point is now the last character, remove that as well
if(str.find('.') == str.size()-1)
{
str = str.substr(0, str.size()-1);
}
}
std::cout << str << std::endl;
}

How to use std::to_string function to format float as “x.0”

std::to_string doesn't support this, and in fact is not generally great for arbitrary floating-point values.

Since your question is tagged c++11, there are two ways I'm aware of. Firstly, you have std::stringstream, which is type safe and will work with arbitrary types:

#include <sstream>

// ...

float number = 30.0f;

std::ostringstream oss;
oss << std::setprecision(1) << number;
std::string result = oss.str();

Alternatively, you have std::snprintf, which requires converting through a char buffer of a given size:

float number = 30.0f;

char buffer[20]; // maximum expected length of the float
std::snprintf(buffer, 20, "%.1f", number);
std::string str(buffer);

From C++17 you may use std::to_chars instead in a similar way to std::snprintf, and from C++20 you may use std::format.

How to remove trailing zeros when using doubles in strings with C#? [duplicate]

string source = "2.4200";
string output = double.Parse(source).ToString();

Credits: here

How to avoid `std::to_string()` making a very small double number to 0? [duplicate]

No, the problem is not precision, but the format. You want to print in scientific format (with exponent) but std::to_string() uses the fixed format by default and I'm not aware of any way to change this.

However, streams use scientific if appropriate or you can force it with std::scientific:

std::ostringstream oss;
oss << 4.7816457028269855e-143;
std::string numberAsString = oss.str(); // stores "4.78165e-143"

Of course you can increae the precision in addition to this.

If for whatever reason you don't want to use scientific format, you can use the fixed format with a high enough precision. "High enough" meaning more than 142 in this case, because there will be 142 leading zeroes:

oss << std::fixed << std::setprecision(142 + x);

But the scientific format is better suited, I guess.



Related Topics



Leave a reply



Submit