1

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.
JosefM
  • 37
  • 4
  • According to http://www.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. – Paul Floyd Aug 22 '17 at 15:43
  • 1
    Not relevant to the question but `main` returns int, not void – Amadeus Aug 22 '17 at 15:51
  • 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. – Hans Passant Aug 22 '17 at 16:27
  • 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. – JosefM Aug 23 '17 at 06:14
  • Possible duplicate of [Math precision requirements of C and C++ standard](https://stackoverflow.com/questions/20945815/math-precision-requirements-of-c-and-c-standard) – phuclv Aug 26 '17 at 05:08
  • 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 results](https://stackoverflow.com/q/31509019/995714) – phuclv Aug 26 '17 at 05:09
  • and [`void main()`](https://stackoverflow.com/q/204476/995714) is prohibited in C++ and C although it's allowed in free-standing C implementations – phuclv Aug 26 '17 at 05:22
  • another possible duplicate: [`exp` precision between Mac OS and Windows](https://stackoverflow.com/q/15216884/995714) – phuclv Aug 29 '17 at 05:40

2 Answers2

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.

Amadeus
  • 8,690
  • 3
  • 22
  • 30
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.

JosefM
  • 37
  • 4
  • This is documented here https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/get-fma3-enable-set-fma3-enable?view=vs-2017 "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." – 0kcats Jan 23 '19 at 21:16