I write a mathematical function to be benchmark function in my optimization algorithm.
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;
}
but it has different result in console application and windows application when the input is SolomonFunction(new double[] { -4.74641638144941E+151, -6.49440696607247E+153, -1.0998592442531E+153, 3.58027097738642E+149, 6.28490996716059E+152 })
in console application the result is 6,616968044816507E+152
in windows application the result is -4,09139395927863E+154
Is there something different that I need to do in a windows application that I don't need to do in a console application? Or am I fundamentally misunderstanding something?
In the platform that produces “-4,09139395927863E+154”, the Math.Cos routine is broken. It apparently uses a processor instruction that does not support operands outside [−2−63, +2−63].
Since I do not use C#, here is a C program that reproduces the correct behavior:
#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));
}
When run with Apple Clang 11 on macOS 10.14.6, this produces “6.616968044816507e+152”. Looking at the calculations, we can see that sum must be huge and the result should be entirely dominated by 0.1 * Math.Sqrt(sum). Since the range of cosine on real numbers in [−1, +1], the 1 - Math.Cos(…) part of the formula should have negligible effect, regardless of the argument to Math.Cos. So this seems like a reasonable result.
Considering the other result, “-4,09139395927863E+154”, we see it is impossible for the formula to produce a negative result when correctly calculated. 1 - Math.Cos(…) should be in [0, 2], and 0.1 * Math.Sqrt(sum) should never be negative, so their sum should be non-negative.
This incorrect result is entirely explained by a defective Math.Cos. Suppose, when the argument is huge, Math.Cos returns its argument instead of its cosine. We can reproduce this with the C code above b y using return 1 - (2 * M_PI * sqrt(sum)) + 0.1 * sqrt(sum);, where cos has been removed, leaving just its argument. Running this produces the output “-4.091393959278625e+154”, matching the reported output (with rounding to a different number of digits), confirming the hypothesis.
This is consistent with behavior of the FCOS instruction. Intel 64 and IA-32 Architecture Software Developer‘s Manual, combined volumes, December 2017, page 906, says, for 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.
Thus, when the cosine argument is out of the supported range (−263 to +263), executing FCOS leaves the argument in the register that is also used for the result. Then Math.Cos apparently uses this value for the result.