开发者

How to format numbers to significant digits using STL

开发者 https://www.devze.com 2023-04-08 14:32 出处:网络
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 anyt

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 ;
}
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号