【问题标题】:Print float/double without trailing zeros? [duplicate]打印不带尾随零的浮点数/双精度数? [复制]
【发布时间】:2013-03-16 19:08:44
【问题描述】:

有几个与此相关的问题,但我还没有看到一个正确回答这个问题的问题。我想打印一个浮点数,但我希望小数位数是自适应的。举个例子:

0      -> 0
1234   -> 1234
0.1234 -> 0.1234
0.3    -> 0.3

令人讨厌的是,%f 说明符只会打印到固定精度,因此它会在所有未达到该精度的数字上添加尾随零。有些人建议使用 %g 说明符,它适用于一组数字,但它会切换到一些数字的科学记数法,如下所示:

printf("%g", 1000000.0); // prints 1e+06

如何打印没有不必要的零的浮点数,同时仍然保持 printf 对实际具有小数分量的数字的标准精度?

【问题讨论】:

  • @AlexeyFrunze 我不认为这是重复的,因为它比我想要的要复杂得多。我可以四舍五入到几位数,我只是不想要任何尾随零。
  • 但这就是你所要求的,引用:How can I print floating-point numbers without the unnecessary zeros, but also without rounding or truncating the value?
  • @AlexeyFrunze 啊,我做到了,我的错。这更多是指类似问题的“答案”,建议使用%.2f 或类似问题。我已经对其进行了编辑以删除该语言。
  • 但是现在语言很模糊。定义fairly good accuracy for numbers that need it
  • @AlexeyFrunze 我的意思是 printf 对实际具有小数部分的数字的标准精度(我相信 6 个小数位)。同样,我不希望它超级准确,我对 printf 的准确性很好,我只是讨厌所有尾随零。

标签: c


【解决方案1】:

使用snprintf 打印到临时缓冲区,然后手动删除尾随'0' 字符。没有其他方法既正确又相当容易实现。

【讨论】:

  • 好主意。但0.1234 将在 IEEE 双精度中表示为 0.12339999999999999580335[...]。没有尾随的“0”字符。
  • 四舍五入到机器精度的几倍,然后修剪零。
  • @flyingOwl, snprintf 会为您解决这些问题。这就是重点。
  • 没有snprintf,十进制四舍五入非常困难
  • @R.. 从技术上讲,这不是截断而不是四舍五入吗?还是我错过了什么?
【解决方案2】:

问题是使用 IEEE 标准 754 表示法,浮点值(带有小数部分)永远不能有“尾随零”。

尾随零表示对于某些整数 x、n,小数值可以写为x/10^n。但是对于某些整数 x、n,唯一可以用该标准表示的分数形式为 x/2^n

所以你写的 0.1234 用字节 0x3D 0xFC 0xB9 0x24 来表示。这是:

Sign = 0
Exponent = 01111011 (which means -4)
Significand: 1.11111001011100100100100

有效数字表示:1 + 1/2 + 1/4 + 1/8 + 1/16 + 0/32 + 0/64 + 1/128 + 0/256 + 1/512 + 1/1024 + 1 /2048 + 0/4096 + 0/8192 + ...

如果你执行这个计算,你会得到 1.974400043487548828125。

所以号码是+ 1.974400043487548828125 * 2^(-4) = 0.1234000027179718

(当然,我是用电脑计算的,所以它可能出于同样的原因关闭......)

如您所见,计算机不想为您决定要在 4 位(仅)之后而不是在 9 位 (0.123400002) 之后砍掉这个数字。关键是计算机不会将此数字视为 0.1234,并带有无限数量的尾随零。

所以我认为没有比 R. 更好的方法了。

【讨论】:

  • 明白。所以也许我想四舍五入说,小数点后 8 位,即 0.12340000。我仍然希望能够在没有额外零的情况下打印这个舍入值。
【解决方案3】:

试试:

printf("%.20g\n", 1000000.0); // = 1000000

这将在 20 位有效数字后切换为科学记数法(默认为 6 位“%g”):

printf("%.20g\n", 1e+19); // = 10000000000000000000
printf("%.20g\n", 1e+20); // = 1e+20

但要小心双精度:

printf("%.20g\n", 0.12345);   // = 0.12345000000000000417
printf("%.15g\n", 0.12345);   // = 0.12345

【讨论】:

  • 没有骰子。 printf("%.15g", 0.00001) 仍然打印 1e-05
【解决方案4】:

我使用已经提到的方法自己编写了一个小函数来执行此操作,这里是使用测试仪。我不能保证它没有错误,但我认为没问题。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

char *convert(char *s, double x);

int main()
{
    char str[3][20];
    printf("%s\n%s\n%s\n", convert(str[0], 0),
          convert (str[1], 2823.28920000),
          convert (str[2], 4.000342300));

}

char *convert(char *s, double x)
{
    char *buf = malloc(100);
    char *p;
    int ch;

    sprintf(buf, "%.10f", x);
    p = buf + strlen(buf) - 1;
    while (*p == '0' && *p-- != '.');
    *(p+1) = '\0';
    if (*p == '.') *p = '\0';
    strcpy(s, buf);
    free (buf);
    return s;
}

输出:

0
2823.2892
4.0003423

【讨论】:

    【解决方案5】:

    你可以这样打印:

    printf("%.0f", 1000000.0);
    

    如需更详细的答案,请查看here

    【讨论】:

    • printf("%.0f", 0.1234); 打印“0”。我不认为 OP 想要那样。
    • “我怎样才能打印没有不必要的零的浮点数,又不舍入或截断值?”是打印 0 的答案,因为 is 在点(小数部分)之后不打印
    • 这会截断数字,不是吗?
    • @Metabble 如果您打印一个变量,那么该格式不会改变您的变量。它只是一种打印数字的格式
    • @nabroyan 问题特别指出 0.1234 应该打印为 0.1234。那不是你的答案在做什么。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-10-13
    • 1970-01-01
    • 1970-01-01
    • 2012-06-09
    • 2019-05-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多