muh*_*rom 5 c# math floating-point .net-core visual-studio-2019
我编写了一个数学函数作为我的优化算法中的基准函数。
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;
}
Run Code Online (Sandbox Code Playgroud)
但当输入为时,它在控制台应用程序和 Windows 应用程序中具有不同的结果SolomonFunction(new double[] { -4.74641638144941E+151, -6.49440696607247E+153, -1.0998592442531E+153, 3.58027097738642E+149, 6.28490996716059E+152 })
在控制台应用程序中,结果是6,616968044816507E+152
在 Windows 应用程序中,结果是-4,09139395927863E+154
是否有一些我需要在 Windows 应用程序中执行但在控制台应用程序中不需要执行的操作?或者我从根本上误解了什么?
在生成 \xe2\x80\x9c-4,09139395927863E+154\xe2\x80\x9d 的平台中,例程Math.Cos被破坏。它显然使用不支持 [\xe2\x88\x922 \xe2\x88\x9263 , +2 \xe2\x88\x9263 ] 之外的操作数的处理器指令。
由于我不使用 C#,因此这里有一个重现正确行为的 C 程序:
\n#include <math.h>\n#include <stdio.h>\n\nstatic double SolomonFunction(size_t length, double *x)\n{\n double sum = 0;\n for (int i = 0; i < length; i++)\n sum += x[i] * x[i];\n\n return 1 - cos(2 * M_PI * sqrt(sum)) + 0.1 * sqrt(sum);\n}\n\n\n#define NumberOf(a) (sizeof (a) / sizeof *(a))\n\n\nint main(void)\n{\n double x[] = { -4.74641638144941E+151, -6.49440696607247E+153, -1.0998592442531E+153, 3.58027097738642E+149, 6.28490996716059E+152 };\n printf("%.16g\\n", SolomonFunction(NumberOf(x), x));\n}\nRun Code Online (Sandbox Code Playgroud)\n当在 macOS 10.14.6 上与 Apple Clang 11 一起运行时,会生成 \xe2\x80\x9c6.616968044816507e+152\xe2\x80\x9d。从计算结果来看,我们可以看到它sum一定很大,而且结果应该完全由 主导0.1 * Math.Sqrt(sum)。由于实数的余弦范围在 [\xe2\x88\x921, +1] 中,因此无论 的1 - Math.Cos(\xe2\x80\xa6)参数如何,公式的这一部分应该具有可以忽略不计的影响Math.Cos。所以这似乎是一个合理的结果。
考虑到另一个结果 \xe2\x80\x9c-4,09139395927863E+154\xe2\x80\x9d,我们发现该公式在正确计算时不可能产生负结果。1 - Math.Cos(\xe2\x80\xa6)应该在 [0, 2] 中,并且0.1 * Math.Sqrt(sum)永远不应该为负数,因此它们的总和应该是非负数。
这个不正确的结果完全可以用缺陷来解释Math.Cos。假设当参数很大时,Math.Cos返回其参数而不是余弦。我们可以使用上面的 C 代码重现这一点return 1 - (2 * M_PI * sqrt(sum)) + 0.1 * sqrt(sum);,其中cos已被删除,仅保留其参数。运行此命令会生成输出 \xe2\x80\x9c-4.091393959278625e+154\xe2\x80\x9d,与报告的输出匹配(四舍五入到不同的位数),从而证实了假设。
这与指令的行为一致FCOS。Intel 64 和 IA-32 架构软件开发人员\xe2\x80\x98s 手册,合并卷,2017 年 12 月,第 906 页,表示FCOS:
\n\n如果源操作数超出可接受的范围,则 FPU 状态字中的 C2 标志被置位,并且寄存器 ST(0) 中的值保持不变。
\n
因此,当余弦参数超出支持的范围(\xe2\x88\x922 63到 +2 63)时,执行FCOS会将参数保留在也用于结果的寄存器中。然后Math.Cos显然使用这个值作为结果。