Ano*_*use 8 c++ floating-point infinite-loop
这是我的源代码:
#include <iostream>
#include <cmath>
using namespace std;
double up = 19.0 + (61.0/125.0);
double down = -32.0 - (2.0/3.0);
double rectangle = (up - down) * 8.0;
double f(double x) {
return (pow(x, 4.0)/500.0) - (pow(x, 2.0)/200.0) - 0.012;
}
double g(double x) {
return -(pow(x, 3.0)/30.0) + (x/20.0) + (1.0/6.0);
}
double area_upper(double x, double step) {
return (((up - f(x)) + (up - f(x + step))) * step) / 2.0;
}
double area_lower(double x, double step) {
return (((g(x) - down) + (g(x + step) - down)) * step) / 2.0;
}
double area(double x, double step) {
return area_upper(x, step) + area_lower(x, step);
}
int main() {
double current = 0, last = 0, step = 1.0;
do {
last = current;
step /= 10.0;
current = 0;
for(double x = 2.0; x < 10.0; x += step) current += area(x, step);
current = rectangle - current;
current = round(current * 1000.0) / 1000.0;
//cout << current << endl;
} while(current != last);
cout << current << endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
它的作用是计算曲线之间的面积.main()中有一个循环 - 其目的是在3个小数位内尽可能精确地计算值.
它没用.为了调试,我添加了唯一注释的行.我想知道循环内部发生了什么.
//cout << current << endl;
Run Code Online (Sandbox Code Playgroud)
当线路在那里 - 当我取消注释它 - 一切正常.当它不是 - 循环似乎是无限的.
神圣的编译器,为什么?
这不是我所知道的不精确浮点数的问题.当我在其中输出当前值时,一切都完成了4次循环内容的重复.
Jos*_*ley 22
@ Skizz的评论给出了可能的问题,但要详细说明:
浮点数学很棘手,特别是经常出现舍入误差.诸如1/1000.0(您的round调用结果)之类的数字无法以浮点精确表示.
更复杂的是,一方面速度与另一方面一致,直观的结果之间存在权衡.例如,Intel处理器的FPU以80位扩展精度格式存储值,而C/C++ double通常为64位.为了提高性能,编译器可能会将值保留在FPU中,作为80位临时值,即使这会产生与将其截断为64位时所得到的结果不同的结果.
启用调试语句后,current可能会将其存储到内存中,将其截断为64位,这样可以直接与之比较last.
禁用调试语句后,current很可能存储在FPU寄存器中的80位值,因此它永远不会相等last,只要last是64位值并且它们都试图存储不精确的浮点表示x/1000.0.
解决方案是使用浮点比较和一些允许的错误(因为直接检查与浮点的相等性几乎不是一个好主意).
补充说明:我没有查看汇编输出以验证是否是这种情况; 如果你愿意,你可以自己做.如果我启用优化,我只能重现问题.您可以通过调整编译器标志来选择一致性而不是速度来"修复"该错误,但正确的解决方案是使用不精确的比较而不是直接检查相等性.