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
时,就会出现问题。 float
s 存储一个指数和一个 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 位。
我想计算 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
时,就会出现问题。 float
s 存储一个指数和一个 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 位。