Double string formatting with ostringstream
I want to convert a double to a string by rounding to two decimal digits after the dot. I want 1.009 to display as "1.01" and 1.0 as "1". This is what I tried:
std::ostringstream oss;
oss << std::fixed << std::setprecision(2) << std::noshowpoint << 1.0;
It outputs "1.00" even though I never set the width or even did it std::noshowpoint
. How do you achieve the desired performance?
source to share
The best decision:
inline double twodec(double n) { return floor(n * 100 + 0.5) / 100; }
oss << twodec(1.0) << ' ' << twodec(1.009);
Discussion
From http://www.cplusplus.com/reference/ios/fixed/ (emphasis mine)
When floatfield is set to a fixed value, float values are written using fixed point notation, which means that the value is represented by exactly as many digits in the fractional part as defined by the precision field, and no exponential part.
So, "fixed" won't work.
However, the only ways I can think of to do what you want:
- first round the number to the desired precision (i.e.
floor(n * 100 + 0.5) / 100
), then use the default presentation (i.e., do not specify fixed or scientific or precision - iffixed
either doesscientific
, clear them with firststd::cout.unsetf(std::ios::floatfield)
). - dynamically adjusting the precision based on the maximum total number of numeric digits you want to see before and after the point (this is what the precision indicates); for this you could determine how many digits the part takes up to the decimal point (perhaps using a database of 10) and add 2
- streaming the result to
ostringstream
, and then removing the trailing 0s and any '.' (pretty disgusting).
source to share
This is my final solution based on Tony's answer:
template <typename T>
std::string stringForNumber( T f, int precision /* = 0 */, bool fixedWidth /*= false*/ )
{
std::ostringstream ss;
ss.setf(std::ios_base::fixed);
if (precision > 0)
ss << std::setprecision(precision);
ss << f;
std::string str(ss.str());
if (!fixedWidth) // Removing trailing 0
{
const auto pointLocation = str.find_first_of(".,");
if (pointLocation != std::string::npos)
{
const auto lastZeroPos = str.find_last_of('0');
const auto lastNotZeroPos = str.find_last_not_of('0');
if (lastNotZeroPos == pointLocation) // Integer number
str.erase(pointLocation);
else if (lastZeroPos != std::string::npos && lastNotZeroPos != std::string::npos && pointLocation < lastZeroPos && lastNotZeroPos < lastZeroPos)
{
str.erase(lastNotZeroPos+1);
}
}
}
return str;
}
source to share