Rosenbrock 检验函数计算的准确性

Accuracy of Rosenbrock's test function calculation

我想计算 Rosenbrock 的检验函数

我已经实现了以下 C/C++ 代码

#include <stdio.h>

/********/
/* MAIN */
/********/
int main()
{
    const int N = 900000;

    float *x = (float *)malloc(N * sizeof(float));
    for (int i=0; i<N; i++) x[i] = 3.f;

    float sum_host = 0.f;
    for (int i=0; i<N-1; i++) {
        float temp = (100.f * (x[i+1] - x[i] * x[i]) * (x[i+1] - x[i] * x[i]) + (x[i] - 1.f) * (x[i] - 1.f));
        sum_host = sum_host + temp;
        printf("%i %f %f\n", i, temp, sum_host);
    }
    printf("Result for Rosenbrock's test function calculation = %f\n", sum_host);

}

由于x数组初始化为3.f,那么每个求和项应该是3604.f,所以最后涉及899999项的求和应该是3243596396。然而,我得到的结果是3229239296,绝对误差是14357100。如果我测量两个连续的部分求和之间的差异,我发现早期的部分求和是 3600.f,然后最后的部分求和下降到 3584,而它应该始终是 3604.f ].

如果我使用Kahan求和算法作为

sum_host = 0.f;
float c        = 0.f;
for (int i=0; i<N-1; i++) {
    float temp = (100.f * (x[i+1] - x[i] * x[i]) * (x[i+1] - x[i] * x[i]) + (x[i] - 1.f) * (x[i] - 1.f)) - c;
    float t    = sum_host + temp;
    c          = (t - sum_host) - temp;
    sum_host = t;
}

我得到的结果是 3243596288,绝对误差要小得多 108

我很确定我看到的这种效果应该归因于浮点运算的精度。有人可以证实这一点并向我解释发生这种情况的机制吗?

典型的 float 仅适用于 7 位精度。重复将 3604 加到比它大 100000 倍的数字上并不能很好地累加较小的有效数字。

使用double.

您在每次迭代中都准确地计算了 temp = 3604.0f。当您尝试将 3604.0f 添加到其他内容并将结果四舍五入到最接近的 float 时,就会出现问题。 floats 存储一个指数和一个 23 位尾数,这意味着任何 1 位相距超过 24 位的结果都将四舍五入为不同于它本身的值。

注意3604 = 901 * 4 901的二进制展开为1110000101;一旦你开始将 temp 添加到大于 2^24 * 4 = 67108864 的值,你就会开始看到舍入。(当你 运行 代码时也会发生这种情况;它开始打印出 3600 作为差异当 sum_host 超过 67108864 时连续 sum_host 之间。)当你将 temp 添加到大于 2^26 * 4 的东西时,你会开始看到更多的舍入;那时,第二小的“1”位也被吞没了。

请注意,在您进行 Kahan 求和后,sum_host 是您报告的内容,而 c-108。这大致是因为 c 正在跟踪下一个最重要的 24 位。