gcc - GCC 编译器:进行浮点运算时的奇怪行为,浮点值饱和到 65536,其中浮点为 4 个字节
问题描述
[我试图计算一个浮点乘法,我观察到该值已经饱和到 65536 并且没有更新。
问题仅在于以下代码。] 1
我用在线 GCC 编译器试过这个问题仍然是一样的。
这与浮点精度有什么关系?编译器是否在运行期间优化了我的浮点精度?
有没有我可以添加的编译器标志来解决这个问题?
谁能指导我如何解决这个问题?
附上代码供参考
#include <stdio.h>
int main()
{
float dummy1, dummy2;
unsigned int i =0;
printf("Hello World");
printf("size of float = %ld\n", sizeof(dummy1));
dummy2 = 0.0;
dummy1 =65535.5;
dummy2 = 60.00 * 0.00005;
for( i= 0; i< 300; i++)
{
dummy1 = dummy1 + dummy2;
printf("dummy1 = %f %f\n", dummy1, dummy2);
}
return 0;
};
解决方案
(此答案假定 IEEE-754 单精度和双精度二进制格式用于float
和double
。)
60.00 * 0.00005
用double
算术计算并产生 0.003000000000000000062450045135165055398829281330108642578125。当它存储在 中时dummy2
,它被转换为 0.0030000000260770320892333984375。
在循环中,dummy1
最终达到值 65535.99609375。然后,当dummy1
和dummy2
相加时,用实数算术计算的结果将是 65535.9990000000260770320892333984375。这个值在float
格式中是不可表示的,所以它被四舍五入到格式中可表示的最接近的值float
,这就是+
运算符产生的结果。
浮点格式中最接近的可表示值是 65535.99609375 和 65536。由于 65536 更接近 65535.9990000000260770320892333984375,因此它是结果。
在下一次迭代中,添加了 65536 和 0.0030000000260770320892333984375。实数运算结果将为 65536.0030000000260770320892333984375。这在 中也无法表示float
。最接近的可表示值是 65536 和 65536.0078125。同样 65536 更接近,所以它是计算结果。
从那时起,循环总是产生 65536 作为结果。
您可以通过使用double
算术或dummy1
在每次迭代中重新计算而不是在迭代之间累积舍入误差来获得更好的结果:
for (i = 0; i < 300; ++i)
{
dummy1 = 65535.5 + i * 60. * .00005;
printf("%.99g\n", dummy1);
}
请注意,因为dummy1
是 a float
,所以它不具有区分序列的某些连续值所需的精度。例如,上面的输出包括:
65535.9921875 65535.99609375 65535.99609375 65536 65536.0078125 65536.0078125 65536.0078125 65536.015625 65536.015625 65536.015625
推荐阅读
- javascript - 尝试将道具值传递给状态,刷新后在构造函数中不起作用
- python - 将 Flask-Admin CRUD 视图嵌入站点
- c - 在 C/C++ 中是否可以为结构内的偏移量导出链接器符号?
- javascript - 为什么 setState 在函数之后使用前一个对象?
- sql - 每 5 分钟自动执行 SQL 语句
- node.js - React 呈现一个空白页面而不是 JSON 提要
- java - Redmine / Java native:通过脚本更新查询。克隆 Fiddler 请求
- concurrency - 控制 F# 中可能发生冲突的异步操作
- node.js - 有条件地更新猫鼬查询中的项目
- angular-material - 垫选择面板最小宽度