printf中浮点数的精度

15

众所周知,当你使用printf输出float值时,存在精度限制。
然而,有一种技巧可以增加输出的精度,下面这个例子展示了这种技巧:

#include <stdio.h>

int main()
{
    float f = 1318926965;        /* 10 random digits */
    printf("%10.f\n", f);        /* prints only 8 correct digits */
    printf("%10d\n", *(int*)&f); /* prints all digits correctly */
    return 0;
}

我的问题是,为什么人们不经常使用这个技巧?


1
因为潜在的未定义行为是不好的。(编辑:从技术上讲,它可能不是未定义的行为,因为我不确定标准对此的规定。但是,这可能会导致字节序问题。) - Corbin
9
愿意为一个好的愚人节玩笑点个赞! - Henrik
它显然给了我 x.x。 - Corbin
1
你应该使用联合体来制作一个无可挑剔的愚人节玩笑。 - Pascal Cuoq
或者一些宏让我的操作变得完全不清楚?不,我不想混淆我的程序。有时候最好的方法就是将事情放在明面上隐藏起来。 - Mr Lister
1
@MrLister,我不是说你的程序太易读了,而是说它太未定义了。明天查一下“严格别名规则”。 - Pascal Cuoq
3个回答

11

愚人节?

你的“随机数”1318926965在十进制和浮点形式下具有相同的基础表示。

尝试另一个值,比如10。它将打印为:

        10
1092616192

因此,回答你的问题:

and my question is, why don't people use this trick more often?

因为一年中只有愚人节这一天... 其他日子玩笑就不管用了...


4
尝试使用不同的数字,比如2318926965,来尝试相同的技巧。
#include <stdio.h>

int main()
{
    float f = 2318926965;        /* 10 random digits */
    printf("%10.f\n", f);        /* prints only 8 correct digits */
    printf("%10d\n", *(int*)&f); /* prints all digits correctly */
    return 0;
}

$ gcc -Wall -O3  t.c
t.c: In function ‘main’:
t.c:7:5: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
$ ./a.out 
2318926848
1326069764

我并没有看到您的“技巧”在精度方面有所提高,因为它依赖于您平台上浮点数的位表示(就我所理解的而言)。
这个帖子中还有其他一些“神奇浮点数”和生成它们的方法。

这取决于IEEE 754中浮点数的位表示,而不是“你的平台”。(除了可能的字节顺序问题,在任何真实世界的机器上都不存在。)在我看来,像这样的代码唯一不好的地方是不当的类型转换,应该用联合替换(虽然有些风险,但所有编译器都有意支持),或者使用memcpy(100%合法的C语言)。 - R.. GitHub STOP HELPING ICE
如果IEEE 754是强制性的,那么__STDC_IEC_559__定义有什么用处? - Mat
这并非强制性要求,但考虑到C标准允许不符合IEEE标准的实现方式,以及在浮点运算方面没有其他可靠依据,如果程序想要在非IEEE数学系统上实现可移植性,使用浮点运算将是一个巨大的错误。(当然,你可以在仅用于单个非IEEE数学实现的非可移植程序中使用它。) - R.. GitHub STOP HELPING ICE
谢谢提供有趣的链接! - ysap

0

精度的限制在于浮点表示,而不是printf(),这是一个错误的前提。

此外,单精度浮点数只能保证正确到6位精度,所以“技巧”只会欺骗自己;在一般情况下它是行不通的。

如果你想要10位浮点数,那么你应该使用双精度,它可以保证15位精度。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接