How to format numbers to significant digits using STL
I'm trying to format numbers to a specific number of significant digits using C/C++ and preferably STL. I've seen examples of doing this in Javascript (toPrecision()) and .Net, but I can't find anything on doing this in C/C++. I want to create a function something like this:
std::string toPrecision(double value, int significantDigits) {
std::string formattedString;
// magic happens here
return formattedString;
}
So that it produces results like this:
toPrecision(123.4567, 2) --> "120"
toPrecision(123.4567, 4) --> "123.4"
toPrecision(123.4567, 5) --> "123.45"
Does anyone know a good way to do this? I'm considering dumping t开发者_如何学Pythonhe whole number into a string and then just scanning through it to find the non-zero digits and count them off in some intelligent way, but that seems cumbersome.
I could also download the source code to one of the browsers and just see what their toPrecision function looks like, but I think it would take me all day to work through the unfamiliar code. Hope someone can help!
Stolen from another question:
#include <string>
#include <sstream>
#include <cmath>
#include <iostream>
std::string toPrecision(double num, int n) {
https://stackoverflow.com/questions/202302/rounding-to-an-arbitrary-number-of-significant-digits
if(num == 0) {
return "0";
}
double d = std::ceil(std::log10(num < 0 ? -num : num));
int power = n - (int)d;
double magnitude = std::pow(10., power);
long shifted = ::round(num*magnitude);
std::ostringstream oss;
oss << shifted/magnitude;
return oss.str();
}
int main() {
std::cout << toPrecision(123.4567, 2) << "\n";
std::cout << toPrecision(123.4567, 4) << "\n";
std::cout << toPrecision(123.4567, 5) << "\n";
}
Check out setprecision() in iomanip. That should do what you are looking for on the double, then just convert to string
Print it to an ostringstream, setting the floating-point formatting parameters as appropriate.
The method above with a ceil(log10(x)) is perfectly legit to determine the number of digit of the integer part of a number. But I feel it's a bit heavy on CPU to call for maths functions just to set a number of digit.
Isn't that simpler to convert the floating value into a string with too many digits, then to work on the string itself?
Here is what I'd try (with Qt instead of the STL):
QString toPrecision(double num, int n) {
QString baseString = QString::number(num, 'f', n);
int pointPosition=baseString.indexOf(QStringLiteral("."));
// If there is a decimal point that will appear in the final result
if (pointPosition != -1 && pointPosition < n)
++n ; // then the string ends up being n+1 in length
if (baseString.count() > n) {
if (pointPosition < n) {
baseString.truncate(n);
} else {
baseString.truncate(pointPosition);
for (int i = n ; i < baseString.count() ; ++i)
baseString[i]='0';
}
} else if (baseString.count() < n) {
if (pointPosition != -1) {
for (int i = n ; i < baseString.count() ; ++i)
baseString.append('0');
} else {
baseString.append(' ');
}
}
return baseString ;
}
But the question was about the STL.. So, let's rewrite it that way:
std::string toPrecision(double num, size_t n) {
std::ostringstream ss;
ss << num ;
std::string baseString(ss.str());
size_t pointPosition=baseString.find('.');
if (pointPosition != std::string::npos && pointPosition < n)
++n ;
if (baseString.length() > n) {
if (pointPosition < n) {
baseString.resize(n);
} else {
baseString.resize(pointPosition);
for (size_t i = n ; i < baseString.length() ; ++i)
baseString[i]='0';
}
} else if (baseString.length() < n) {
if (pointPosition != std::string::npos) {
baseString.append(n-baseString.length(),'0');
} else {
baseString.append(n-baseString.length(),' ');
}
}
return baseString ;
}
精彩评论