I have always found iomanip confusing and counter intuitive. I need help.
A quick internet search finds (https://www.vedantu.com/maths/precision) "We thus consider precision as the maximum number of significant digits after the decimal point in a decimal number" (the emphasis is mine). That matches my understanding too. However I wrote a test program and:
stm << std::setprecision(3) << 5.12345678;
std::cout << "5.12345678: " << stm.str() << std::endl;
stm.str("");
stm << std::setprecision(3) << 25.12345678;
std::cout << "25.12345678: " << stm.str() << std::endl;
stm.str("");
stm << std::setprecision(3) << 5.1;
std::cout << "5.1: " << stm.str() << std::endl;
stm.str("");
outputs:
5.12345678: 5.12
25.12345678: 25.1
5.1: 5.1
If the precision is 3 then the output should be:
5.12345678: 5.123
25.12345678: 25.123
5.1: 5.1
Clearly the C++ standard has a different interpretation of the meaning of "precision" as relates to floating point numbers.
If I do:
stm.setf(std::ios::fixed, std::ios::floatfield);
then the first two values are formatted correctly, but the last comes out as 5.100
.
How do I set the precision without padding?
The setprecision is a manipulator function in C++ which is used to sets the decimal precision of floating-point values on output operations. This setprecision is the built-in function defined in <iomanip> header file in C++.
You can't set precision of a float or double variable. Only for the input/output of such a variable. But you shouldn't be using float or double for monetary values to begin with, due to the fact that floating-point values/math is inprecise.
C++ has a library called iomanip which provides us a function named setprecision() using which we can change the number of significant digits.
You can try using this workaround:
decltype(std::setprecision(1)) setp(double number, int p) {
int e = static_cast<int>(std::abs(number));
e = e != 0? static_cast<int>(std::log10(e)) + 1 + p : p;
while(number != 0.0 && static_cast<int>(number*=10) == 0 && e > 1)
e--; // for numbers like 0.001: those zeros are not treated as digits by setprecision.
return std::setprecision(e);
}
And then:
auto v = 5.12345678;
stm << setp(v, 3) << v;
Another more verbose and elegant solution is to create a struct like this:
struct __setp {
double number;
bool fixed = false;
int prec;
};
std::ostream& operator<<(std::ostream& os, const __setp& obj)
{
if(obj.fixed)
os << std::fixed;
else os << std::defaultfloat;
os.precision(obj.prec);
os << obj.number; // comment this if you do not want to print immediately the number
return os;
}
__setp setp(double number, int p) {
__setp setter;
setter.number = number;
int e = static_cast<int>(std::abs(number));
e = e != 0? static_cast<int>(std::log10(e)) + 1 + p : p;
while(number != 0.0 && static_cast<int>(number*=10) == 0)
e--; // for numbers like 0.001: those zeros are not treated as digits by setprecision.
if(e <= 0) {
setter.fixed = true;
setter.prec = 1;
} else
setter.prec = e;
return setter;
}
Using it like this:
auto v = 5.12345678;
stm << setp(v, 3);
I don't think you can do it nicely. There are two candidate formats: defaultfloat
and fixed
. For the former, "precision" is the maximum number of digits, where both sides of the decimal separator count. For the latter "precision" is the exact number of digits after the decimal separator.
So your solution, I think, is to use fixed
format and then manually clear trailing zeros:
#include <iostream>
#include <iomanip>
#include <sstream>
void print(const double number)
{
std::ostringstream stream;
stream << std::fixed << std::setprecision(3) << number;
auto string=stream.str();
while(string.back()=='0')
string.pop_back();
if(string.back()=='.') // in case number is integral; beware of localization issues
string.pop_back();
std::cout << string << "\n";
}
int main()
{
print(5.12345678);
print(25.12345678);
print(5.1);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With