获取 printf 以打印所有浮点数

Get printf to print all float digits

我对 printf("%f", M_PI) 的行为感到困惑。它打印出 3.141593,但 M_PI3.14159265358979323846264338327950288。为什么 printf 这样做,我怎样才能让它打印出整个浮点数。我知道 %1.2f 格式说明符,但如果我使用它们,那么我会得到一堆未使用的 0,并且输出很难看。我想要浮点数的全部精度,但不需要任何额外的东西。

Why does printf do this, and how can I get it to print out the whole float.

默认情况下,printf() 函数对 %f%F 格式说明符采用 6 的精度。来自 C11 (N1570) §7.21.6.1/p8 fprintf 函数(强调我的前进):

If the precision is missing, it is taken as 6; if the precision is zero and the # flag is not specified, no decimal-point character appears. If a decimal-point character appears, at least one digit appears before it. The value is rounded to the appropriate number of digits.

因此 call 等同于:

printf("%.6f", M_PI);

这与 "whole float" 完全不同,至少不像您想象的那样直接。 double objects 很可能以二进制 IEEE-754 double precision 表示形式存储。您可以使用 %a%A 格式说明符查看确切的表示形式,将其打印为十六进制浮点数。例如:

printf("%a", M_PI);

输出为:

0x1.921fb54442d18p+1

你可以认为是 "whole float"。

如果您需要的只是 "longest decimal approximation",这是有道理的,那么使用 <float.h> header 中的 DBL_DIG。 C11 5.2.4.2.2/p11 C浮动类型的特性:

number of decimal digits, q, such that any floating-point number with q decimal digits can be rounded into a floating-point number with p radix b digits and back again without change to the q decimal digits

例如:

printf("%.*f", DBL_DIG-1, M_PI);

可以打印:

3.14159265358979

一旦将一段文本转换为浮点数或双精度数,"all"数字就不再是一个有意义的概念。例如,计算机无法知道它转换了“3.14”或“3.14000000000000000275”,而它们恰好都产生了相同的浮点数。您只需根据您对所涉及数字精度的了解,选择适合您任务的位数。

如果您想打印尽可能多的数字,可能会被格式清楚地表示,浮点数大约为 7 位数字,双精度数大约为 15 位,但这是一个近似值。

您可以使用 sprintf 以过高的显示精度将浮点数打印到字符串,然后使用函数将 trim 0 传递给 printf 使用 %s 来显示它。概念证明:

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

void trim_zeros(char *x){
    int i;
    i = strlen(x)-1;
    while(i > 0 && x[i] == '0') x[i--] = '[=10=]';
}

int main(void){
    char s1[100];
    char s2[100];
    sprintf(s1,"%1.20f",23.01);
    sprintf(s2,"%1.20f",M_PI);
    trim_zeros(s1);
    trim_zeros(s2);
    printf("s1 = %s, s2 = %s\n",s1,s2);
    //vs:
    printf("s1 = %1.20f, s2 = %1.20f\n",23.01,M_PI);
    return 0;
}

输出:

s1 = 23.010000000000002, s2 = 3.1415926535897931
s1 = 23.01000000000000200000, s2 = 3.14159265358979310000

这说明这种方法可能不是您想要的。如果小数部分中连续零的数量超过特定长度(可以作为参数传递给 trim_zeros),而不是简单地 trimming zeros,您可能想要截断。此外 - 您可能想要确保 23.0 显示为 23.0 而不是 23。(因此可能在小数点后保留一个零)。这主要是概念验证 — 如果您对 printf 不满意,请使用 sprintf 然后修改结果.