此方法中堆栈溢出的原因(浮点数学)

Raj*_*war 8 c++ stack-overflow

我偶尔会在此方法中获得stackoverflow异常.

double norm_cdf(const double x) {
    double k = 1.0/(1.0 + 0.2316419*x);
    double k_sum = k*(0.319381530 + k*(-0.356563782 + k*(1.781477937 + k*(-1.821255978 + 1.330274429*k))));

    if (x >= 0.0) {
        return (1.0 - (1.0/(pow(2*M_PI,0.5)))*exp(-0.5*x*x) * k_sum);
    } else {
        return 1.0 - norm_cdf(-x);
    }
}
Run Code Online (Sandbox Code Playgroud)

为什么我可能得到它的任何建议?我可以采取哪些措施来纠正错误?

Dav*_*ave 24

你的问题是什么时候x不是数字.NAN >= 0.0是假的,-NAN >= 0.0也是假的.

正如其他人所建议的那样,您可以特别检查NAN,但我建议简化一些事情:

static double norm_cdf_positive(const double x) {
    double k = 1.0/(1.0 + 0.2316419*x);
    double k_sum = k*(0.319381530 + k*(-0.356563782 + k*(1.781477937 + k*(-1.821255978 + 1.330274429*k))));

    return (1.0 - (1.0/(pow(2*M_PI,0.5)))*exp(-0.5*x*x) * k_sum);
}

double norm_cdf(const double x) {
    if (x >= 0.0) {
        return norm_cdf_positive(x);
    } else {
        return 1.0 - norm_cdf_positive(-x);
    }
}
Run Code Online (Sandbox Code Playgroud)

这样做的好处是编译器可以对其行为做出更明智的假设.请注意,我已将"内部"函数标记为静态(将其范围限制为当前编译单元).您还可以使用未命名的命名空间.(编辑:实际上Timothy Shields有一种更简单的方法来删除递归,它将所有内容保存在一个函数中)

  • 不是数字.例如`0.0/0.0`或`sqrt(-1.0)`(除非你使用复数).它会不时地出现在你不会想到的地方,除非你努力避免它. (4认同)

Tim*_*lds 15

可能这种方法只是打破骆驼背部的稻草.这个函数最多只调用一次,所以不是问题.(编辑:或者是其他人指向的NAN问题,这导致无限递归.)

无论如何,您可以轻松地使函数不递归,这可能是一个更简单的选项.

double norm_cdf(double x) {
    bool negative = x < 0;
    x = abs(x);
    double k = 1.0/(1.0 + 0.2316419*x);
    double k_sum = k*(0.319381530 + k*(-0.356563782 + k*(1.781477937 + k*(-1.821255978 + 1.330274429*k))));

    double result = (1.0/(pow(2*M_PI,0.5)))*exp(-0.5*x*x) * k_sum;
    if (!negative)
        result = 1.0 - result;
    return result;
}
Run Code Online (Sandbox Code Playgroud)

  • +1用于指出不需要递归的明显方法. (2认同)

Ker*_* SB 8

xNaN时,递归将永远不会终止.添加一个检查:std::isnan在C++ 11中,或者是懒惰的x != x,或者是文档和责任用户.如果你选择处理NaN,传播它可能是一个明智的选择.

  • @Rajeshwar:对于浮点数是否为"NaN"来说,这是一个肮脏的测试.`NaN`是唯一不等于自身的浮点值. (3认同)