#include <stdio.h>
int main() {
printf("%.14f\n", 0.0001f * 10000000.0f); // 1
printf("%.14f\n", 0.001f * 1000000.0f); // 2
printf("%.14f\n", 0.01f * 100000.0f); // 3
return 0;
}
这段代码的输出是:
1000.00000000000000
1000.00006103515625
1000.00000000000000
我知道,小数不能用浮点数精确表示。但是为什么第 1 行和第 3 行计算正确而第 2 行却没有?你对这里发生的事情有清楚的解释吗?
答案 0 :(得分:5)
有时累积舍入(在 OP 样本中每个 3 步)的结果与数学/十进制相同,有时则不同。 @Steve Summit、@Steve Summit
对这里发生的事情有一个清晰的解释吗?
每行代码有 3 个潜在的舍入步骤:
float
的源代码。回忆一下常见的 float
的形式:some_limited_integer * 2some_power.
float
乘法四舍五入
打印四舍五入到小数点后 14 位的 float
*1。
对于printf("%.14f\n", 0.0001f * 10000000.0f); // 1
将 0.0001f
编码为 float
,值为 0.0000999999974737875163555145263671875
0.0000999999974737875163555145263671875 * 10000000.0 --> 999.999974737875163555145263671875 > {1}p0>{1}
1000.0 --> float
。
对于"1000.00000000000000"
将 printf("%.14f\n", 0.001f * 1000000.0f); // 2
编码为 0.001f
,值为 0.001000000047497451305389404296875
0.001000000047497451305389404296875 * 1000000.0 --> 1000.000047497451305389404296875 --> 四舍五入到最接近的{056} --} 100000.0.000000.0
1000.00006103515625 --> float
。
在#1 中,四舍五入是向下的,然后向上 - 趋于取消。
在 #2 中,四舍五入是向上和向上的 - 导致明显的 double rounding 效果。
粗略地说,每一步都可能注入多达 1/2 ULP 的错误。
其他注意事项:1) 替代舍入模式。以上使用舍入到最近。 2)弱库。以上假设质量为 float
。
*1 在 OP 的样本中,没有舍入误差。一般来说,用 "1000.00006103515625"
打印 printf()
可以四舍五入。
答案 1 :(得分:4)
回答这个问题的另一种方式是说您没有得到一个“错误”的答案和两个“正确”的答案。您实际上得到了三个“正确”的答案,其中“正确”的意思是“与预期一样好”。
Type float
只能提供大约 7 位十进制数字的精度。因此,对于 1000 范围内的数字,这是小数点后三位。所以把程序改成这样:
printf("%.3f\n", 0.0001f * 10000000.0f);
printf("%.3f\n", 0.001f * 1000000.0f);
printf("%.3f\n", 0.01f * 100000.0f);
输出为:
1000.000
1000.000
1000.000
没有差异,所有答案显然都是正确的。
或者,用指数表示法,小数点前一位,小数点后 6 位。
printf("%.6e\n", 0.0001f * 10000000.0f);
printf("%.6e\n", 0.001f * 1000000.0f);
printf("%.6e\n", 0.01f * 100000.0f);
给予
1.000000e+03
1.000000e+03
1.000000e+03
同样,所有答案都一样。
这可能看起来像是“作弊”:我们碰巧知道在我们可以看到的右边的数字中有一些“有趣”的事情,所以以这种方式压制它们是不是错了,并且让它看起来所有的答案都一样?我不打算回答这个问题,除了指出当你在做浮点运算时,几乎总是右边有一些东西,你正在四舍五入和压制——这只是程度的问题。