问题
I want to format a floating point value to n significant digits but never using scientific notation (even if it would be shorter).
The format specification %f
doesn't deal in significant digits, and %g
will sometimes give me scientific notation (which is inappropriate for my use).
I want values in the form "123", "12.3", "1.23" or "0.000000123"
.
Is there an elegant way to do this using C++ or boost?
回答1:
The best way I know (and use it in my own code) is
#include <string>
#include <math.h>
#include <sstream>
#include <iomanip>
int round(double number)
{
return (number >= 0) ? (int)(number + 0.5) : (int)(number - 0.5);
}
std::string format(double f, int n)
{
if (f == 0) {
return "0";
}
int d = (int)::ceil(::log10(f < 0 ? -f : f)); /*digits before decimal point*/
double order = ::pow(10., n - d);
std::stringstream ss;
ss << std::fixed << std::setprecision(std::max(n - d, 0)) << round(f * order) / order;
return ss.str();
}
c++11 has std::round so you won't need my version of with a new compiler.
The trick I'm exploiting here is to get the precision you want by taking the base 10 log to count the number of digits before the decimal and subtracting this from the precision you want.
It satisfies @Mats Petersson's requirement too, so will work in all cases.
The bit I don't like is the initial check for zero (so the log function doesn't blow up). Suggestions for improvement / direct editing of this answer most welcome.
回答2:
std::fixed
and std::setprecision
(and <iomanip>
in general) are your friends.
std::cout << 0.000000123 << '\n';
prints 1.23e-07
and
std::cout << std::setprecision(15) << std::fixed << 0.000000123 << '\n';
prints 0.000000123000000
Just remember that floating-point numbers have limited precision, so
std::cout << std::fixed << 123456789012345678901234567890.0 << '\n';
will print 123456789012345677877719597056.000000
(probably not what you want)
回答3:
I think you'll have to remove trailing zeros by yourself :
string trimString(string str)
{
string::size_type s;
for(s=str.length()-1; s>0; --s)
{
if(str[s] == '0') str.erase(s,1);
else break;
}
if(str[s] == '.') str.erase(s,1);
return str;
}
Usage :
double num = 0.000000123;
stringstream ss;
ss << num;
ss.str("");
ss << std::setprecision(15) << std::fixed << num; // outputs 0.000000123000000
string str;
ss >> str;
str = trimString(str);
cout << str << endl; // outputs 0.000000123
Put together :
string format(int prec, double d) {
stringstream ss;
ss << d;
ss.str("");
ss << std::setprecision(prec) << std::fixed << d;
string str;
ss >> str;
string::size_type s;
for(s=str.length() - 1; s > 0; --s)
{
if(str[s] == '0') str.erase(s,1);
else break;
}
if(str[s] == '.') str.erase(s,1);
return str;
}
Usage :
double num = 0.000000123;
cout << format(15, num) << std::endl;
If someone knows a better way ...
来源:https://stackoverflow.com/questions/17211122/formatting-n-significant-digits-in-c-without-scientific-notation