Windows vs Linux - math result difference - c++

I have program in C++. If I run same part of code, Linux and Windows are giving different results.
#include <cmath>
#include <cfloat>
#include <cstdio>
#define MPI 3.141592653589793238462
#define DEG_TO_RAD(x) ((x) * 0.0174532925)
#define cot(x) (1.0 / tan(x))
#define sec(x) (1.0 / cos(x))
double p1 = DEG_TO_RAD(35);
double p2 = DEG_TO_RAD(65);
double lambertN = log(cos(p1) * sec(p2));
lambertN /= (log(tan(0.25 * MPI + 0.5 * p2) * cot(0.25 * MPI + 0.5 * p1)));
double t = tan(0.25 * MPI + 0.5 * p1);
double lambertF = cos(p1) * pow(t, lambertN);
//---------------------------
//specify input coordinates in degrees
double lon = 160.25;
double lat = 245.75;
double longitude = DEG_TO_RAD(lon - 10);
double latitude = DEG_TO_RAD(lat);
double c = cot(0.25 * MPI + 0.5 * latitude);
double lambertPhi = lambertF * pow(c, lambertN);
printf("%f", lambertPhi); // here I got different results on Win and Linux
On Windows, I got correct result (or it seems so, because final result is OK).
On Linux, I got NaN or some very small numbers in comaprison to Windows.
What am I missing ?
EDIT #1:
Windows - Visual Studio 2010 - build via GUI
Linux - gcc version 4.4.7 20120313 (Red Hat 4.4.7-4) (GCC) - built with makefile, flags: CFLAGS = -lm -lstdc++ -Wall -O2
Both systems are 64bit
PS:
If anyone is interested, this is part of Lambert-Conic projection equation.

First, there is no real reason to expect the same results,
unless you take active steps to ensure specific results. The
C++ language definition allows intermediate results to use
extended precision. Typically, if a compiler does this (and
doing it is very frequent on an Intel architectures), The
extended precision will be truncated to standard double
precision when the compiler stores to memory. And when it
stores to memory will depend on the internals of the compiler
(and probably even on the degree of optimization).
In the case of Intel, modern chips contain several floating
point processors: the older FPU uses extended precision, where
as the newer SSE variants don't. But the newer SSE variants
aren't available on older processors. By default, g++ (the
Linux compiler) uses the older FPU, to work everywhere, but
Visual C++, as far as I can tell, uses SSE. This means that by
default, you will get different results. Both compilers have
extensive options for changing this, but if you're running the
default configuration, I would not expect g++ and Visual to
give the same results.

I get nans for lat values between 90 and 270. That seems reasonable because in that range the cot() will return a negative number which later cannot simply be raised to a fractional power with pow.
The question remains why you get different results on Windows for this. But unless you provide information about the concrete input values I cannot say more.

Related

The result of using variables and constants is not the same in c++

Please look at the picture
Wrong answer
Right answer
The first which I used the variable i, and I get the wrong answer
The second which I did not use i, the result is right.
My compiler is MinGW GCC 4.7.2 32-bit, and my computer is win10 64-bit.
I feel very strange about the different results, Is this loss of accuration?
this is my file after using gcc test.cpp -S
the first https://paste.ubuntu.com/p/TXjCPyWC9G/
the second https://paste.ubuntu.com/p/FJXQDbyRFq/
Thanks in advance, by the way, forgive my poor english..
First, I summarize the two ways of computing the same numerical expression (given in images by the OP):
Correct result (“Right answer”, 9255):
int tmp = 10000 - (10000 - 8000) * 0.2 - 1500 * 0.03 - 3000 * 0.1;
Wrong result (“Wrong answer”, 9254):
int i = 10000;
int tmp = i - (i - 8000) * 0.2 - 1500 * 0.03 - 3000 * 0
The expression without the variable (“Right answer”) is computed at compile time (optimization, to save runtime), the expression with the variable i (“Wrong answer”) is computed at runtime. Obviously the compiler and the compiled program use different arithmetics (different rounding). (The number 0.1 has no exact binary floating point representation, so rounding is necessary.)
I cannot say why these two systems use different rounding. When I compile your “Wrong” example with gcc on my machine it yields the right answer.

sine result depends on C++ compiler used

I use the two following C++ compilers:
cl.exe : Microsoft (R) C/C++ Optimizing Compiler Version 19.00.24210 for x86
g++ : g++ (Ubuntu 5.2.1-22ubuntu2) 5.2.1 20151010
When using the built-in sine function, I get different results. This is not critical, but sometimes results are too significants for my use. Here is an example with a 'hard-coded' value:
printf("%f\n", sin(5451939907183506432.0));
Result with cl.exe:
0.528463
Result with g++:
0.522491
I know that g++'s result is more accurate and that I could use an additional library to get this same result, but that's not my point here. I would really understand what happens here: why is cl.exe that wrong?
Funny thing, if I apply a modulo of (2 * pi) on the param, then I get the same result than g++...
[EDIT] Just because my example looks crazy for some of you: this is a part of a pseudorandom number generator. It is not important to know if the result of the sine is accurate or not: we just need it to give some result.
You have a 19-digit literal, but double usually has 15-17 digit precision. As a result, you can get a small relative error (when converting to double), but big enough (in the context of sine calculation) absolute error.
Actually, different implementations of the standard library have differences in treating such large numbers. For example, in my environment, if we execute
std::cout << std::fixed << 5451939907183506432.0;
g++ result would be 5451939907183506432.000000
cl result would be 5451939907183506400.000000
The difference is because versions of cl earlier than 19 have a formatting algorithm that uses only a limited number of digits and fills the remaining decimal places with zero.
Furthermore, let's look at this code:
double a[1000];
for (int i = 0; i < 1000; ++i) {
a[i] = sin(5451939907183506432.0);
}
double d = sin(5451939907183506432.0);
cout << a[500] << endl;
cout << d << endl;
When executed with my x86 VC++ compiler the output is:
0.522491
0.528463
It appears that when filling the array sin is compiled to the call of __vdecl_sin2, and when there is a single operation, it is compiled to the call of __libm_sse2_sin_precise (with /fp:precise).
In my opinion, your number is too large for sin calculation to expect the same behavior from different compilers and to expect the correct behavior in general.
I think Sam's comment is closest to the mark. Whereas you're using a recentish version of GCC/glibc, which implements sin() in software (calculated at compile time for the literal in question), cl.exe for x86 likely uses the fsin instruction. The latter can be very imprecise, as described in the Random ASCII blog post, "Intel Underestimates Error Bounds by 1.3 quintillion".
Part of the problem with your example in particular is that Intel uses an imprecise approximation of pi when doing range reduction:
When doing range reduction from double-precision (53-bit mantissa) pi the results will have about 13 bits of precision (66 minus 53), for an error of up to 2^40 ULPs (53 minus 13).
According to cppreference:
The result may have little or no significance if the magnitude of arg is large
(until C++11)
It's possible that this is the cause of the problem, in which case you will want to manually do the modulo so that arg is not large.

Maximum Precision for C++ with Eigen3

I'm using the awesome Eigen3 library to write a MATLAB MEX file. But I am experiencing some accuracy issues (compared to MATLAB), even when using long double.
The most critical computation seems to be the one where I compute a probability according to the normal distribution.
Here is the code snippet:
p.fill( 1/( M_PIl * sigma * sigma ) );
p.array() *= ( - 0.5/pow( sigma, 2.0 ) * ( mu.array() - x.array() ).array().square() ).array().exp();
where x, p and mu are Eigen::Matrix< long double, Dynamic, 1 >. Usually these vectors have a length of 3000.
What are possible steps I can take to get the maximum possible precision?
What are the correct GCC compiler flags I can use to force 80 bit precision wherever possible?
P.S: I compile the C++ code (in MATLAB with MEX) with gcc 4.9 and my linux reports the following available instruction sets: Intel MMX, Intel SSE, Intel SSE2, Intel SSE3, Intel SSE4
Edit:
I tried what #Avi Ginsburg suggested below and compiled it using the following command:
mex -g -largeArrayDims '-I/usr/include/eigen3' CXXFLAGS='-DEIGEN_DONT_VECTORIZE -std=c++11 -fPIC' test.cpp
with double and long double and each of these options gives me the same error with respect to the solution from MATLAB.
I'm hazarding a guess here. You are using SSE instructions with your array calculations, most notably, ...array().exp(). I'm pretty sure there is no extended precision with SSE, hence the differences between MATLAB and Eigen.
By default Eigen uses a faster but slightly less accurate implementation of several mathematical functions, including the exponential. You can explicitly disable these optimizations by compiling with the -DEIGEN_FAST_MATH=0 option.
If you use gcc as your compiler also make sure that you don't use the -Ofast or -ffast-math options, as these can result in reduced precision.
If you want to compute the probability density of a (1 dimensional) normal distribution, the factor at the beginning should be 1/std::sqrt( 2* M_PIl * sigma * sigma ).
Also, the p.fill() at the beginning of your snippet is inefficient. Just write this in one line:
p = (1/std::sqrt(2*M_PIl*sigma*sigma)) *
( -0.5/(sigma*sigma) * (mu-x).array().square() ).exp();
N.B.: If you are only performing element-wise operations on your arrays, consider declaring them as Eigen::Array<...> instead of Eigen::Matrix<...>. The template parameters are the same, also the binary layout is the same, but you don't need to write .array() every time you want to make element-wise operations.

cmath function that adds and multiplies at once x*y+z

Is there a function in the cmath library which given 3 numbers x, y and z returns x*y+z?
fma which stands for Fused Multiply Add was introduced in C99 and C++11:
#include <cassert>
#include <cmath>
int main() {
assert(std::fabs(std::fma(2.0, 3.0, 4.0) - (2.0 * 3.0 + 4.0)) < 0.001);
}
Probable rationales:
IEEE 754-2008 seems to have added support for the operation, requiring that it be done with a single rounding instead of two.
Thanks to #Lưu for bringing it up in the comment.
some popular archs such as ARM and x86 have one cycle fma instructions, so in theory an arch optimized compilers / stdlibs could use those.
I do not know if in practice modern compilers are doing this optimization already.
For integers on X86, FMA could already be done with the LEA instruction: I think the innovation is the fact that it uses double.

Why are doubles added incorrectly in a specific Visual Studio 2008 project?

Trying to port java code to C++ I've stumbled over some weird behaviour. I can't get double addition to work (even though compiler option /fp:strict which means "correct" floating point math is set in Visual Studio 2008).
double a = 0.4;
/* a: 0.40000000000000002, correct */
double b = 0.0 + 0.4;
/* b: 0.40000000596046448, incorrect
(0 + 0.4 is the same). It's not even close to correct. */
double c = 0;
float f = 0.4f;
c += f;
/* c: 0.40000000596046448 too */
In a different test project I set up it works fine (/fp:strict behaves according to IEEE754).
Using Visual Studio 2008 (standard) with No optimization and FP: strict.
Any ideas? Is it really truncating to floats? This project really needs same behaviour on both java and C++ side. I got all values by reading from debug window in VC++.
Solution: _fpreset(); // Barry Kelly's idea solved it. A library was setting the FP precision to low.
The only thing I can think of is perhaps you are linking against a library or DLL which has modified the CPU precision via the control word.
Have you tried calling _fpreset() from float.h before the problematic computation?
Yes, it's certainly truncating to floats. I get the same value printing float f = 0.4 as you do in the "inaccurate" case. Try:
double b = 0.0 + (double) 0.4;
The question then is why it's truncating to floats. There's no excuse in the standard for treating 0.0 + 0.4 as a single-precision expression, since floating point literals are double-precision unless they have a suffix to say otherwise.
So something must be interfering with your settings, but I have no idea what.