printf和scanf如何处理浮点精度格式?

时间:2010-09-24 18:45:25

标签: c floating-point double precision

考虑以下代码片段:

float val1 = 214.20;
double val2 = 214.20;

printf("float : %f, %4.6f, %4.2f \n", val1, val1, val1);
printf("double: %f, %4.6f, %4.2f \n", val2, val2, val2);

哪个输出:

float : 214.199997,  214.199997, 214.20 | <- the correct value I wanted 
double: 214.200000,  214.200000, 214.20 |

我理解214.20具有无限的二进制表示。第一行的前两个元素具有预期值的近似值,但最后一个元素似乎根本没有近似值,这导致我提出以下问题:

scanffscanfprintffprintf(等)函数如何处理精度格式?

如果没有提供精确度,printf会打印出一个近似值,但是%4.2f会得到正确的结果。你能解释一下这些函数用来处理精度的算法吗?

3 个答案:

答案 0 :(得分:10)

问题是,214.20无法用二进制表示法表示完全。几十进制数可以。因此存储了近似值。现在当你使用printf时,二进制表示将变成十进制表示,但它再次无法准确表达,只是近似。

正如您所注意到的,您可以给printf一个精度来告诉它如何舍入十进制近似值。如果你没有给它一个精度,那么假定精度为6(详见手册页)。

如果你在上面的示例中使用%.40f作为浮点数而%.40lf用于double,则会得到以下结果:

214.1999969482421875000000000000000000000000
214.1999999999999886313162278383970260620117

它们是不同的,因为有了双倍,有更多的位可以更好地接近214.20。但正如你所看到的,当用十进制表示时,它们仍然很奇怪。

我建议您阅读Wikipedia article on floating point numbers,了解有关浮点数如何工作的更多见解。优秀的阅读也是What Every Computer Scientist Should Know About Floating-Point Arithmetic

答案 1 :(得分:4)

由于您询问了scanf,您应该注意的一件事是POSIX需要printf和后续scanf(或strtod)来重建原始值完全只要打印出足够有效的数字(至少DECIMAL_DIG,我相信)。普通C当然没有这样的要求; C标准几乎允许浮点运算提供实现者喜欢的任何结果,只要它们记录它。但是,如果您的意图是将浮点数存储在文本文件中以便稍后读回,那么最好使用C99 %a说明符以十六进制格式打印它们。通过这种方式,它们将是准确的,并且不会混淆序列化/反序列化过程是否会失去精度。

请记住当我说“重建原始值”时,我的意思是传递给printf的变量/表达式中保存的实际值,而不是您在源文件中写入的原始小数舍入到编译器的最佳二进制表示。

答案 2 :(得分:1)

scanf将输入值四舍五入到最接近的可精确表示的浮点值。正如DarkDust的答案所示,在单精度中,最接近的可精确表示的值低于精确值,而在双精度中,最接近的可精确表示的值高于精确值。