Search code examples
c++iomanipsignificant-digits

Significant figures in C++


I've written a program that calculates values in a series and all of the values are particularly lengthy doubles. I want to print these values each displaying 15 significant figures. Here's some code that illustrates the issue I'm having:

#include <iostream>
#include <iomanip>
using namespace std;

int main()
{
    double x = 0.12345678901234567890;
    double y = 1.12345678901234567890;
    cout << setprecision(15) << fixed << x << "\t" << y << "\n";
    return 0;
}

With just setprecision trailing zeros are not shown so I added fixed as I have seen in other answers on this site. However, now I just seem to have 15 decimal places and for values that aren't 0.something this is not what I want. You can see this from the output of the above:

0.123456789012346       1.123456789012346

The first number has 15 sig figs but the second has 16. What can I do to resolve this?

EDIT: I have been specifically asked to use setprecision, so I am unable to try cout.precision.


Solution

  • You can simply use scientific (note the 14 instead of 15):

    std::cout << std::scientific << std::setprecision(14) << -0.123456789012345678 << std::endl;
    std::cout << std::scientific << std::setprecision(14) << -1.234567890123456789 << std::endl;
    
    -1.23456789012346e-01
    -1.23456789012346e+00
    

    or you can use a function:

    #include <iostream>
    #include <vector>
    #include <iomanip>
    #include <string>
    #include <sstream>
    
    enum vis_opt { scientific, decimal, decimal_relaxed };
    
    std::string figures(double x, int nfig, vis_opt vo=decimal) {
        std::stringstream str;
    
        str << std::setprecision(nfig-1) << std::scientific << x;
        std::string s = str.str();
        if ( vo == scientific )
            return s;
        else {
            std::stringstream out;
            std::size_t pos;
    
            int ileft = std::stoi(s,&pos);
    
            std::string dec = s.substr(pos + 1, nfig - 1);
            int e = std::stoi(s.substr(pos + nfig + 1));
    
            if ( e < 0 ) {
                std::string zeroes(-1-e,'0');
                if ( ileft < 0 ) 
                    out << "-0." << zeroes << -ileft << dec;
                else
                    out << "0." << zeroes << ileft << dec;
            } else if ( e == 0) {
                out << ileft << '.' << dec;
            } else if ( e < ( nfig - 1) ) {
                out << ileft << dec.substr(0,e) << '.' << dec.substr(e);
            } else if ( e == ( nfig - 1) ) {
                out << ileft << dec;
            } else {
                if ( vo == decimal_relaxed) {
                    out << s;
                } else {
                    out << ileft << dec << std::string(e - nfig + 1,'0');
                }
            }
    
            return out.str();   
        }
    
    }
    
    int main() {
        std::vector<double> test_cases = {
            -123456789012345,
            -12.34567890123456789,
            -0.1234567890123456789,
            -0.0001234,
            0,
            0.0001234,
            0.1234567890123456789,
            12.34567890123456789,
            1.234567890123456789,
            12345678901234,
            123456789012345,
            1234567890123456789.0,
        };
    
    
        for ( auto i : test_cases) {
            std::cout << std::setw(22) << std::right << figures(i,15,scientific);
            std::cout << std::setw(22) << std::right << figures(i,15) << std::endl;
        }
        return 0;
    }
    

    My output is:

     -1.23456789012345e+14      -123456789012345
     -1.23456789012346e+01     -12.3456789012346
     -1.23456789012346e-01    -0.123456789012346
     -1.23400000000000e-04 -0.000123400000000000
      0.00000000000000e+00      0.00000000000000
      1.23400000000000e-04  0.000123400000000000
      1.23456789012346e-01     0.123456789012346
      1.23456789012346e+01      12.3456789012346
      1.23456789012346e+00      1.23456789012346
      1.23456789012340e+13      12345678901234.0
      1.23456789012345e+14       123456789012345
      1.23456789012346e+18   1234567890123460000