Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use only standard C++ to print any float to exactly N spaces

I have a float that I would like to print to exactly N spaces. For example, with N = 5. I want to get the following numbers printed as so:

0.1        => 0.1
123.456789 => 123.5
-123.45678 => -123 (or -123.)
0.123456   => 0.123
123456.789 => 123456 (obviously, in this case, we cannot print less than 6 digits, if I can detect this, I can also print 12** to indicate the field is too wide.

I have tried many combination including the following:

// Leave 3 extra space: One for negative sign, one for zero, one for decimal
std::cout << std::setiosflags(std::ios::fixed) 
          << std::setprecision(N - 3) 
          << std::setw(N) 
          << input;

But the results are not favorable. Perhaps I am missing something obvious?

The output of the given code for those sample inputs are

 0.10     // not bad 
123.46    // Use 6 instead of 5 spaces
-123.46   // Use 7 instead of 5 spaces
 0.12     // Under-use the spaces
123456.79 // Ok, I guess
like image 210
Dat Chu Avatar asked Oct 26 '25 14:10

Dat Chu


2 Answers

Use Boost's Spirit.Karma for this:

#include <boost/spirit/include/karma.hpp>
namespace karma = boost::spirit::karma;

double d = 12345.6;

// will print: 12345
std::cout << karma::format(
        karma::maxwidth(5)[karma::double_], d
    ) << std::endl;

which will limit the width of your output to 5 in any case. If you want to catch the case where the number would be cut off (as in your example for 123456.7), just perform the appropriate check up front:

if (d < 1e6) {
    // d == 12345 will print: 12345
    std::cout << karma::format(
            karma::maxwidth(5)[karma::double_], d
        ) << std::endl;
}
else {
    // d == 123456 will print: 12***
    std::cout << karma::format(
            karma::left_align(5, '*')[
                karma::maxwidth(2)[karma::double_]
            ], d
        ) << std::endl;
}

And yes, in case you might ask, Boost's Spirit.Karma is written using only Standard C++ features.

like image 103
hkaiser Avatar answered Oct 29 '25 04:10

hkaiser


int main ()
{
  const int N = 4;

  double f = 123.456789;

  // just use default floating-point notation to display N significant digits.
  // no fixed or scientific notation work for your requirement.
  // you can comment setf() line out if there is no one changing ios::floatfield.
  cout.setf(0, ios::floatfield); 
  cout.precision(N);

  cout << f << endl;              // will print 123.5
}

[UPDATE] I should have tested for all cases provided. :D

#include <iostream>
#include <math.h>
using namespace std;

void print_test(double number)
{
    const int N = 5; // should be greater than 2

    double maxIntPart = pow(10.0, number >= 0 ? N - 2 : N - 3);

    double fractpart, intpart;
    fractpart = modf(number , &intpart);

    ios_base::fmtflags prevNotation = cout.setf(0, ios::floatfield);  // use default floating-point notation
    streamsize prevPrecicion = cout.precision();

    if( abs(intpart) > maxIntPart )
    {
        intpart = ceil(number);
        if( abs(intpart) < maxIntPart * 10 )
        {
            cout << intpart << endl;
        }
        else
        {               
            while( abs(intpart) >= maxIntPart )
            {
                intpart /= 10;
            }

            cout << (intpart > 0 ? floor(intpart) : ceil(intpart)) << "**" << endl;
        }
    }
    else
    {
        if ( number < 0 )
        {
            cout.precision(N - 3);
        }
        else if ( intpart == 0)
        {
            cout.precision(N - 2);
        }
        else
        {
            cout.precision(N - 1);
        }
        cout << number << endl;
    }

    cout.setf(prevNotation, ios::floatfield); 
    cout.precision(prevPrecicion);
}


int main ()
{
    print_test(0.1);                // 0.1
    print_test(123.456789);         // 123.5
    print_test(-123.45678);         // -123
    print_test(0.123456);           // 0.123
    print_test(-0.123456);          // -0.12
    print_test(123456.789);         // 123**
    print_test(-123456.789);        // -12**
 }
like image 33
young Avatar answered Oct 29 '25 03:10

young



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!