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

请考虑以下代码段:

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它给出了正确的结果。 你能解释一下这些函数用来处理精度的算法吗?

问题是,214.20无法用二进制表示精确表达。 几十进制数可以。 因此存储了近似值。 现在,当您使用printf时,二进制表示将变为十进制表示,但它再次无法准确表达并且仅仅是近似值。

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

如果您在上面的示例中使用%.40f作为浮点数并使用%.40lf作为double,您将获得以下结果:

 214.1999969482421875000000000000000000000000 214.1999999999999886313162278383970260620117 

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

我建议阅读关于浮点数的维基百科文章,以获得有关浮点数如何工作的更多见解。 一本优秀的读物也是每个计算机科学家应该知道的关于浮点运算的内容

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

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

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