Используйте только стандартный C ++ для печати любого числа с плавающей точкой ровно в N пробелов - PullRequest
3 голосов
/ 19 апреля 2011

У меня есть число с плавающей точкой, которое я хотел бы напечатать ровно в N пробелов.Например, при N = 5. Я хочу, чтобы следующие цифры печатались следующим образом:

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.

Я пробовал много комбинаций, включая следующие:

// 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;

Но результатыне выгодно.Возможно, я упускаю что-то очевидное?

Вывод данного кода для этих вводимых примеров:

 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

Ответы [ 5 ]

4 голосов
/ 19 апреля 2011

Используйте Boost's Spirit.Karma для этого:

#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;

, что ограничит ширину вашего вывода 5 в любом случае.Если вы хотите поймать случай, когда число будет обрезано (как в вашем примере для 123456.7), просто выполните соответствующую проверку заранее:

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

И да, в случае, если вы спросите, Boost'sSpirit.Karma написана с использованием только стандартных возможностей C ++.

2 голосов
/ 19 апреля 2011
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
}

[ОБНОВЛЕНО] Я должен был проверить для всех случаев, предусмотренных. : 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**
 }
1 голос
/ 19 апреля 2011

Кажется, вы хотите напечатать N символов - включая знак минус (если есть) и десятичную точку. setprecision заботится только о количестве цифр после десятичной точки. Итак, небольшая математика может дать вам то, что вы хотите:

#include <cmath>

// determine what precision we need
int precision = N - 1;                      // leave room for the decimal point
if (input < 0) --precision;                 // leave room for the minus sign
precision -= (1 + (int)log10(abs(input)));  // how many digits before the decimal?
if (precision < 0) precision = 0;           // don't go negative with precision

std::cout << std::setiosflags(std::ios::fixed) 
          << std::setprecision(precision) 
          << std::setw(N) 
          << input;

Мне, вероятно, нужно объяснить строку логарифма:

precision -= (1 + (int)log10(abs(input))); // how many digits before the decimal?
  • все, что от 0 до 9, печатается одной цифрой.
  • log10 0-9 даст число меньше 1, которое будет усечено до int 0
  • что-либо из 10-99 занимает две цифры для печати. ​​
  • log10 из 10-99 даст число по крайней мере 1, но меньше 2 - усекает до int 1
  • итак ... log10 (вход) плюс 1 - сколько цифр он печатает
1 голос
/ 19 апреля 2011

Ваши требования не удовлетворяются должным образом с помощью флагов точности / ширины iostream - например, precision считает значащие цифры, а не позиции символов, и 4 запроса на 0,12456 будут отображены на 0,1234, а не на 0,12, который вы запрашиваете (ожидаете ли вы 0,00123456быть "0,00"?).В любом случае, чтобы использовать точность, вам нужно изменить параметр в зависимости от значения. Может быть проще написать свою собственную функцию, как показано ниже ...

Самый простой способ - записать значение в поток строк с особой точностью, затем выполнить сканирование слева направо (записьнаходите ли вы «.») до тех пор, пока не достигнете точки, в которой вы хотите усечь, затем сканируйте справа налево, чтобы удалить конечные 0 или заменить «**», если «.»раньше не встречался.

В противном случае вы можете реализовать собственное преобразование типа double в ASCII, возможно, используя заголовок, такой как <ieee754.h> в linux, чтобы обеспечить union с полями битов над float и double типов;в противном случае используйте математические операции по вашему выбору.

0 голосов
/ 19 апреля 2011

Безобразно, но, возможно, вы хотите:

if(input < pow(10,N+1)) {
  ostringstream sout;
  sout << input;
  cout << setw(N) << sout.str().substr(0,N);
} else cout << (int)input; 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...