浮点舍入效果说明
#include <stdio.h>
int main() {
printf("%.14fn", 0.0001f * 10000000.0f); // 1
printf("%.14fn", 0.001f * 1000000.0f); // 2
printf("%.14fn", 0.01f * 100000.0f); // 3
return 0;
}
神箭
这段代码的输出是:
1000.00000000000000
1000.00006103515625
1000.00000000000000
我知道,小数不能用浮点数准确表示。但是为什么第 1 行和第 3 行计算正确,而第 2 行却没有?你对这里发生的事情有清楚的解释吗?
回答
有时累积舍入(在 OP 样本中每个 3 步)的结果与数学/十进制相同,有时则不同。@史蒂夫峰会,@史蒂夫峰会
详细说明这里发生了什么?
每行代码有 3 个潜在舍入步骤:
-
源代码到
float. 回忆一下常见float的形式: some_limited_integer * 2 some_power。 -
float乘以四舍五入 -
打印
float四舍五入到 14 位小数*1。
为了 printf("%.14fn", 0.0001f * 10000000.0f); // 1
-
代码
0.0001f为float0.0000999999974737875163555145263671875 -
0.0000999999747378751635555145263671875 * 10000000.0 --> 999.999974737875163555145263671875 --> 最接近的 100
float--> 0 -
1000.0 -->
"1000.00000000000000".
为了 printf("%.14fn", 0.001f * 1000000.0f); // 2
-
代码
0.001f为float0.001000000047497451305389404296875 -
0.001000000047497451305389404296875 * 1000000.0 --> 1000.000047497451305389404296875 --> 四舍五入到最接近的
float--> 100610503。 -
1000.00006103515625 -->
"1000.00006103515625".
在#1 中,四舍五入是向下的,然后向上 - 趋于取消。
在 #2 中,四舍五入是向上和向上的 - 导致明显的双舍入效果。
粗略地说,每一步都可能注入高达 1/2 ULP 的错误。
其他注意事项: 1) 替代舍入模式。以上使用舍入到最近。2)弱库。以上假定一个质量printf()。
*1在 OP 的样本中,没有舍入误差。一般情况下,float用"%f"罐头印刷。