Search code examples
c++formatscientific-notationboost-format

Formatting n significant digits in C++ without scientific notation


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?


Solution

  • 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.