仅使用标准 C++将任何浮点打印到恰好 N 个空格

发布于 2024-11-02 08:12:59 字数 866 浏览 0 评论 0原文

我有一个浮点数,我想将其打印到恰好 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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

半世蒼涼 2024-11-09 08:12:59

为此,请使用 Boost 的 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 的 Spirit.Karma 仅使用标准 C++ 编写特征。

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.

淡淡的优雅 2024-11-09 08:12:59
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**
 }
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**
 }
姜生凉生 2024-11-09 08:12:59

您似乎想要打印 N 个字符 - 包括减号(如果有)和小数点。 set precision只关心小数点后的位数。所以,一点数学知识就能得到你想要的东西:

#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 的任何数字都需要一位数字来打印。
  • 0-9 的 log10 会给出一个小于 1 的数字,这会截断为 int 0
  • 10-99 之间的任何数字都需要两位数字来打印。
  • log10 of 10-99 给出的数字至少为 1,但小于 2 - 截断为 int 1
  • 所以... log10(input) 加 1 就是它打印的位数

You seem to want to print N characters - including the minus sign (if any) and decimal point. setprecision only cares about the number of digits after the decimal point. So, a little math can get you what you want:

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

I probably need to explain the logarithm line:

precision -= (1 + (int)log10(abs(input))); // how many digits before the decimal?
  • anything from 0-9 takes one digit to print.
  • log10 of 0-9 would give a number less than 1, which would truncate to int 0
  • anything from 10-99 takes two digits to print.
  • log10 of 10-99 would give a number at least 1, but less than 2 - truncates to int 1
  • so... log10(input) plus 1 is how many digits it prints
别低头,皇冠会掉 2024-11-09 08:12:59

iostream 精度/宽度标志不能很好地满足您的要求 - 例如,精度计算有效数字而不是字符位置,并且 4 个请求 0.123456 映射到 0.1234 而不是您请求的 0.123(您愿意吗?)期望 0.00123456 为“0.00”?)。无论如何,要使用精度,您需要根据值改变参数。 如下所示编写自己的函数可能会更容易...

最简单的方法是以额外的精度将值写入字符串流,然后从左到右扫描(记录是否找到 ' .') 直到到达要截断的位置,然后从右向左扫描以删除尾随 0,或者如果“.”则替换为“**”。之前没有遇到过。

否则,您可以实现自己的双精度到 ASCII 转换,可能使用 Linux 的 之类的标头来提供带有 上的位字段的 union >floatdouble 类型;否则使用您选择的数学运算。

Your requirements aren't catered for well by the iostream precision/width flags - for example, precision counts significant digits not character positions, and 4 requests 0.123456 be mapped to 0.1234 not the 0.123 you request (would you expect 0.00123456 to be "0.00"?). Anyway, to use precision you'd need to vary the parameter depending on the value. It may be easier to write your own function as below...

The easiest way is to write the value to a stringstream with extra precision, then scan left-to-right (recording whether you find a '.') until you reach the point where you'd like to truncate, then scanning right-to-left to remove trailing 0s or to substitute "**" if the "." wasn't encountered earlier.

Otherwise, you can implement your own double-to-ASCII conversion, possibly using a header like linux's <ieee754.h> to provide a union with bit fields over float and double types; otherwise using mathematical operations of your choice.

花开雨落又逢春i 2024-11-09 08:12:59

丑陋,但也许你想要:

if(input < pow(10,N+1)) {
  ostringstream sout;
  sout << input;
  cout << setw(N) << sout.str().substr(0,N);
} else cout << (int)input; 

Ugly, but perhaps you want:

if(input < pow(10,N+1)) {
  ostringstream sout;
  sout << input;
  cout << setw(N) << sout.str().substr(0,N);
} else cout << (int)input; 
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文