浮点数舍入效果说明
Explanation for floating point rounding effect
#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 行不是?您对这里发生的事情有清楚的解释吗?
有时累积舍入(OP 样本中的每个舍入 3 步)的结果与 mathematical/decimal 相同,有时则不同。 ,
a clear explanation of what is going on here in detail?
每行代码有3步潜在舍入:
float
的源代码。回想常见的 float
的形式是:some_limited_integer * 2some_power.
float
四舍五入的乘法
打印float
四舍五入到小数点后14位*1.
对于printf("%.14f\n", 0.0001f * 10000000.0f); // 1
将 0.0001f
编码为 float
,值为 0.0000999999974737875163555145263671875
0.0000999999974737875163555145263671875 * 10000000.0 --> 999.999974737875163555145263671875 --> 四舍五入到最接近的 float
--> 1000.0
1000.0 --> "1000.00000000000000"
.
对于printf("%.14f\n", 0.001f * 1000000.0f); // 2
将 0.001f
编码为 float
,值为 0.001000000047497451305389404296875
0.001000000047497451305389404296875 * 1000000.0 --> 1000.000047497451305389404296875 --> 四舍五入到最接近的 float
--> 1000.00006103[532625]
1000.00006103515625 --> "1000.00006103515625"
.
在 #1 中,四舍五入先是向下,然后向上 - 趋于取消。
在 #2 中,四舍五入越来越大 - 产生了明显的 double rounding 效果。
粗略地说,每个步骤最多可能会注入 1/2 ULP 错误。
其他注意事项:1)替代舍入模式。以上使用四舍五入到最近。 2) 图书馆薄弱。以上假定质量 printf()
.
*1 在OP的样本中,没有舍入误差。一般来说,打印float
和"%f"
可以四舍五入。
另一种回答方式是说您没有得到一个“错误”答案和两个“正确”答案。您实际上得到了三个“正确”的答案,其中“正确”的意思是“和预期的一样好”。
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
同样,所有答案都相同。
这可能看起来像“作弊”:我们碰巧知道在我们能看到的右边的数字中发生了一些“有趣”的事情,所以以这种方式抑制它们是不是错误的,并让所有答案看起来都一样?我不打算回答这个问题,只是想指出当你在做浮点运算时,几乎 总是 右边有一些东西,你是四舍五入和抑制——只是程度的问题。
#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 行不是?您对这里发生的事情有清楚的解释吗?
有时累积舍入(OP 样本中的每个舍入 3 步)的结果与 mathematical/decimal 相同,有时则不同。
a clear explanation of what is going on here in detail?
每行代码有3步潜在舍入:
float
的源代码。回想常见的float
的形式是:some_limited_integer * 2some_power.float
四舍五入的乘法打印
float
四舍五入到小数点后14位*1.
对于printf("%.14f\n", 0.0001f * 10000000.0f); // 1
将
0.0001f
编码为float
,值为 0.00009999999747378751635551452636718750.0000999999974737875163555145263671875 * 10000000.0 --> 999.999974737875163555145263671875 --> 四舍五入到最接近的
float
--> 1000.01000.0 -->
"1000.00000000000000"
.
对于printf("%.14f\n", 0.001f * 1000000.0f); // 2
将
0.001f
编码为float
,值为 0.0010000000474974513053894042968750.001000000047497451305389404296875 * 1000000.0 --> 1000.000047497451305389404296875 --> 四舍五入到最接近的
float
--> 1000.00006103[532625]1000.00006103515625 -->
"1000.00006103515625"
.
在 #1 中,四舍五入先是向下,然后向上 - 趋于取消。
在 #2 中,四舍五入越来越大 - 产生了明显的 double rounding 效果。
粗略地说,每个步骤最多可能会注入 1/2 ULP 错误。
其他注意事项:1)替代舍入模式。以上使用四舍五入到最近。 2) 图书馆薄弱。以上假定质量 printf()
.
*1 在OP的样本中,没有舍入误差。一般来说,打印float
和"%f"
可以四舍五入。
另一种回答方式是说您没有得到一个“错误”答案和两个“正确”答案。您实际上得到了三个“正确”的答案,其中“正确”的意思是“和预期的一样好”。
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
同样,所有答案都相同。
这可能看起来像“作弊”:我们碰巧知道在我们能看到的右边的数字中发生了一些“有趣”的事情,所以以这种方式抑制它们是不是错误的,并让所有答案看起来都一样?我不打算回答这个问题,只是想指出当你在做浮点运算时,几乎 总是 右边有一些东西,你是四舍五入和抑制——只是程度的问题。