c++ exp function different results under x64 on i7-3770 and i7-4790
Asked Answered
W

2

2

When I execute a simple x64 application with the following code, I get different results on Windows PCs with a i7-3770 and i7-4790 CPU.

#include <cmath>
#include <iostream>
#include <limits>

void main()
{
  double val = exp(-10.240990982718174);
  std::cout.precision(std::numeric_limits<double>::max_digits10);
  std::cout << val;
}

Result on i7-3770:

3.5677476354876406e-05

Result on i7-4790:

3.5677476354876413e-05

When I modify the code to call

unsigned int control_word;
_controlfp_s(&control_word, _RC_UP, MCW_RC);

before the exp function call, both CPUs deliver the same results.

My questions:

  • Does anyone have an idea for the reason of the differences between the i7-3770 and i7-4790?
  • Is there a way to set the floating point precision or consistency in a Visual Studio 2015/2017 C++ project for the whole project and not only for the following function call? The "Floating Point Model" setting (/fp) does not have any influence on the results here.
Wary answered 22/8, 2017 at 15:22 Comment(8)
According to ttmath.org/online_calculator the first 20 or so digits should be 0.000035677476354876 393107 (I've added a space to show where the digits start to diverge). The first result is slightly closer.Dennard
Not relevant to the question but main returns int, not voidRegistered
There is almost certainly an Evil DLL getting injected into your process that changes the control word. Do note that _RC_UP is not the default, it is _RC_NEAR. So whatever machine you consider the "good" one is actually the troublemaker. Hunting for that DLL can keep you busy for a while. It is the kind of problem that a disk format tends to solve a lot quicker, especially when it isn't you that has to do it.Specialty
I am aware that this is not the default behavior. But this is the only possiblity I currently found which gives me the same results on both CPU types. Furthermore I tried this on more PCs with different CPUs and I have got the impression that all PCs with Ivy Bridge and prior CPUs deliver the same results (3.5677476354876406e-05) and all Haswell and later CPUs deliver also the same results (3.5677476354876413e-05). Could it be possible, that beginning with Haswell rounding of double precision values under x64 has changed.Wary
Possible duplicate of Math precision requirements of C and C++ standardTorticollis
complex functions like sin, cos, exp are not required to be properly rounded by IEEE-754. Those values differ by only 1ULP which is normal. See sin(45) and cos(45) giving different resultsTorticollis
and void main() is prohibited in C++ and C although it's allowed in free-standing C implementationsTorticollis
another possible duplicate: exp precision between Mac OS and WindowsTorticollis
W
1

I did some further investigations and I found out the following facts:

  • the problem does also occur on Windows with a different compiler (Intel)
  • on a linux system both values are equal

I also posted this question to the Visual Studio Community. I got the information, that Haswell and newer CPUs use FMA3. You can disable this feature with _set_FMA3_enable(0) at the beginning of the program. When I do this, the results are the same.

Wary answered 18/9, 2017 at 8:35 Comment(1)
This is documented here learn.microsoft.com/en-us/cpp/c-runtime-library/reference/… "By default, on X64 platforms, the CRT startup code detects whether the CPU supports FMA3 instructions, and enables or disables the FMA3 implementations in the library."Iceblink
R
2

Assuming that double is coded using IEEE-754, and using this decimal to binary converter, you can see that:

3.5677476354876406e-05 is represented in hexa as 0x3F02B48CC0D0ABA8 3.5677476354876413e-05 is represented in hexa as 0x3F02B48CC0D0ABA9

which differ only in the last bit, probably due round error.

Registered answered 22/8, 2017 at 16:2 Comment(0)
W
1

I did some further investigations and I found out the following facts:

  • the problem does also occur on Windows with a different compiler (Intel)
  • on a linux system both values are equal

I also posted this question to the Visual Studio Community. I got the information, that Haswell and newer CPUs use FMA3. You can disable this feature with _set_FMA3_enable(0) at the beginning of the program. When I do this, the results are the same.

Wary answered 18/9, 2017 at 8:35 Comment(1)
This is documented here learn.microsoft.com/en-us/cpp/c-runtime-library/reference/… "By default, on X64 platforms, the CRT startup code detects whether the CPU supports FMA3 instructions, and enables or disables the FMA3 implementations in the library."Iceblink

© 2022 - 2024 — McMap. All rights reserved.