浮点舍入效果说明

时间:2021-07-20 16:05:27

标签: c floating-point

#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;
}

Godbolt

这段代码的输出是:

1000.00000000000000
1000.00006103515625
1000.00000000000000

我知道,小数不能用浮点数精确表示。但是为什么第 1 行和第 3 行计算正确而第 2 行却没有?你对这里发生的事情有清楚的解释吗?

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

同样,所有答案都一样。

这可能看起来像是“作弊”:我们碰巧知道在我们可以看到的右边的数字中有一些“有趣”的事情,所以以这种方式压制它们是不是错了,并且让它看起来所有的答案都一样?我不打算回答这个问题,除了指出当你在做浮点运算时,几乎总是右边有一些东西,你正在四舍五入和压制——这只是程度的问题。

相关问题