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?
CodePudding user response:
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.