无限循环heisenbug:如果我添加打印输出,它退出 [英] Infinite loop heisenbug: it exits if I add a printout

查看:140
本文介绍了无限循环heisenbug:如果我添加打印输出,它退出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是我的源代码:

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

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) * 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<<电流< endl;
} while(current!= last);

cout<<电流< endl;
return 0;
}

它是计算曲线之间的面积。在main()中有一个循环 - 它的目的是计算精确的值,小数点后3位。



没有用。为了调试,我添加了唯一注释的行。我想知道循环内部发生了什么。

  // cout<电流< endl; 

当行在那里 - 当我取消注释时 - 一切正常。



这不是问题的不精确浮点数,我知道。

解决方案

p> @ Skizz的注释给出了可能的问题,但是要详细说明:



浮点数学是棘手的,特别是,舍入误差经常会出现。诸如1 / 1000.0(您的回合调用的结果)的数字不能以浮点数精确表示。



另一个复杂因素是,一方面速度和另一方面一致,直观的结果之间存在权衡。例如,Intel处理器的FPU以80位扩展精度格式存储值,而C / C ++ double 通常为64位。对于性能来说,编译器可能会将FPU中的值作为80位临时值,即使这会产生与您将其截断为64位时所得到的结果不同的结果。



在启用调试语句的情况下, current 很可能存储到内存中,截断到64位,这允许直接与 / code>。



在禁用调试语句的情况下, current 很可能是一个80位的值在FPU寄存器中,因此只要最后就可以等于 last 是一个64位的值,它们都试图存储 x / 1000.0 的不精确浮点表示。



解决方案是使用浮点比较与一些允许的错误(因为直接检查与浮点数的相等性几乎从不是一个好主意)。



进一步说明:我没有看过汇编输出验证是否如此;你可以自己做,如果你想要的。我只能够重现的问题,如果我启用优化。您可以通过调整编译器标志来选择一致性来修复错误,但是正确的解决方案是使用不精确的比较,而不是直接检查相等性。


This is my source code:

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

What it does is calculating area between the curves. There is a loop in main() - its purpose is to calculate value as precise as it's possible within 3 decimal places.

It didn't work. For the sake of debugging, I added the line which is the only commented one. I wanted to know what's going on inside the loop.

//cout << current << endl;

When the line is there - when I uncomment it - everything works fine. When it's not - the loop seems to be infinite.

Holy Compiler, why?

It's not the matter of imprecise floating-point numbers, which I am aware of. Everything is finished withing 4 repeats of the loop content when I'm outputting current value inside it.

解决方案

@Skizz's comment gives the likely problem, but to elaborate:

Floating point math is tricky, and, in particular, rounding errors can often arise. A number such as 1/1000.0 (the results of your round call) can't be precisely represented in floating point.

A further complication is that there are tradeoffs between speed on the one hand and consistent, intuitive results on the other. For example, an Intel processor's FPU stores values in an 80-bit extended precision format, while a C/C++ double is typically 64 bits. For performance, a compiler may leave values in the FPU, as 80-bit temporaries, even though this can produce different results than what you'd get if you truncated them to 64 bits.

With your debug statement enabled, current is likely stored to memory, truncating it to 64 bits, which permits a direct comparison with last.

With the debug statement disabled, current is likely an 80-bit value stored in an FPU register, and thus it can never equal last, as long as last is a 64-bit value and they're both trying to store an inexact floating point representation of x/1000.0.

The solution is to use a floating point comparison with some allowed error (because a direct check for equality with floating point is almost never a good idea).

Further notes: I haven't looked at the assembly output to verify that this is the case; you can do this yourself if you want. I'm only able to reproduce the problem if I enable optimizations. You may be able to "fix" the bug by tweaking compiler flags to choose consistency over speed, but the correct solution is to use an inexact comparison instead of a direct check for equality.

这篇关于无限循环heisenbug:如果我添加打印输出,它退出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆