为什么这个使用余弦的数值方程在控制台应用程序和 windows 应用程序之间产生不同的结果?

Why does this numeric equation using a cosine produce a different result between a console application and windows application?

我写了一个数学函数作为优化算法中的基准函数。

public static double SolomonFunction(double[] x)
    {
        double f;
        double sum = 0;
        for (int i = 0; i < x.Length; i++)
        {
            sum += x[i] * x[i];
        }

        f = 1 - Math.Cos(2 * Math.PI * Math.Sqrt(sum)) + 0.1 * Math.Sqrt(sum);
        return f;
    }

但当输入为 SolomonFunction(new double[] { -4.74641638144941E+151, -6.49440696607247E+153, -1.0998592442531E+153, 3.58027097738642E+149, 6.28490996716059E+152 })

时,它在控制台应用程序和 windows 应用程序中有不同的结果

在控制台应用程序中,结果是 6,616968044816507E+152 在 windows 应用程序中,结果是 -4,09139395927863E+154

是否需要在 windows 应用程序中执行一些我不需要在控制台应用程序中执行的不同操作?还是我从根本上误解了什么?

在生成“-4,09139395927863E+154”的平台中,Math.Cos的套路被打破了。它显然使用了不支持 [−2−63, +2−63].

之外的操作数的处理器指令

由于我不使用 C#,这里有一个重现正确行为的 C 程序:

#include <math.h>
#include <stdio.h>

static double SolomonFunction(size_t length, double *x)
{
    double sum = 0;
    for (int i = 0; i < length; i++)
        sum += x[i] * x[i];

    return 1 - cos(2 * M_PI * sqrt(sum)) + 0.1 * sqrt(sum);
}


#define NumberOf(a) (sizeof (a) / sizeof *(a))


int main(void)
{
    double x[] = { -4.74641638144941E+151, -6.49440696607247E+153, -1.0998592442531E+153, 3.58027097738642E+149, 6.28490996716059E+152 };
    printf("%.16g\n", SolomonFunction(NumberOf(x), x));
}

当 运行 在 macOS 10.14.6 上使用 Apple Clang 11 时,会生成“6.616968044816507e+152”。看一下计算,可以看出sum一定很大,结果应该完全被0.1 * Math.Sqrt(sum)支配了。由于 [−1, +1] 中实数的余弦值域,公式的 1 - Math.Cos(…) 部分的影响可以忽略不计,无论 Math.Cos 的参数如何。所以这似乎是一个合理的结果。

考虑到另一个结果,“-4,09139395927863E+154”,我们看到公式在正确计算时不可能产生负结果。 1 - Math.Cos(…) 应该在 [0, 2] 之间,而 0.1 * Math.Sqrt(sum) 永远不应该是负数,所以它们的总和应该是 non-negative.

这个不正确的结果完全可以用有缺陷的 Math.Cos 来解释。假设,当参数很大时,Math.Cos returns 它的参数而不是它的余弦。我们可以使用 return 1 - (2 * M_PI * sqrt(sum)) + 0.1 * sqrt(sum); 在上面的 C 代码中重现这一点,其中 cos 已被删除,只留下它的参数。 运行 这会产生输出“-4.091393959278625e+154”,与报告的输出匹配(四舍五入到不同的位数),证实了假设。

这与 FCOS 指令的行为一致。 Intel 64 和 IA-32 架构软件开发人员手册,合卷,2017 年 12 月,第 906 页,对于 FCOS:

If the source operand is outside the acceptable range, the C2 flag in the FPU status word is set, and the value in register ST(0) remains unchanged.

因此,当余弦参数超出支持范围(-263至+263)时,执行FCOS 将参数留在寄存器中,该寄存器也用于结果。然后 Math.Cos 显然使用这个值作为结果。