3

I'm asking this question because I'm trying to achieve bitwise (hash) equality between Visual Studio 2017 (cl.exe) and gcc 5.4.0. The problematic function makes use of sin() and cos(). All variables are double, and FMAD is also relevant.

I've been reading extensively on SO and the web about floating point determinism, reproducibility, and lock-stock MP game design. I'm aware that single-compiler, single-build determinism is not hard, but I am attempting 2-compiler, single-build determinism.

Efficiency is not a concern here. I just want the results to match.

I ask because I hope to narrow my concerns for what to test/try.

Are these things relevant for x86_64 processors and builds?

  1. functions that control the x87 fpu
  2. XPFPA_{DECLARE,RESTORE,etc}
  3. "<"fpu_control.h>, _FPU_SINGLE, _FPU_DOUBLE, etc.
  4. _controlfp_s(), _PC24, _PC53, _PC_64

I ask because I have read that platforms with SSE (x86_64) default to using SSE for floating point, so fpu control functions should be irrelevant?

I have found this and this to be most informative. This MSDN article says setting the floating point precision mask is not supported on x64 arch. And this SO post says SSE has fixed precision.

My testing has shown that /fp:{strict,precise,fast} are not changing the hashes. Neither is optimization level. So I'm hoping to narrow my focus to sin, cos.

Tyson Hilmer
  • 741
  • 7
  • 25
  • 2
    gcc and MSVC use different standard libraries, and thus potentially different implementations of trig functions. On top of this different hardware implementations of SIMD can give different answers. You may have more luck using the same 3rd party maths library (e.g. MKL) on both platforms. In my experience you'll be lucky if you ever consistently get exact bitwise results across both platforms. Perhaps have a read of this - https://software.intel.com/en-us/articles/introduction-to-the-conditional-numerical-reproducibility-cnr – virgesmith Oct 19 '17 at 11:49
  • An mcve demonstrating the issue would be useful. – rustyx Oct 19 '17 at 11:51
  • I think if you want sin/cos be the same, then you'll need to use the same implementation (i.e. find/create an implementation of these functions, and include them into your project). It's because SSE doesn't have sin/cos, so these are implemented by some algorithm. Different libraries will give you different guarantees (the only exception is if they guarantee the most precise result with the same rounding). – geza Oct 19 '17 at 11:56
  • You may find [this Random ASCII blog entry](https://randomascii.wordpress.com/2013/07/16/floating-point-determinism/) useful. – user694733 Oct 19 '17 at 12:04
  • for example, crlibm provides correctly rounded results for sin/cos (I haven't used this library) – geza Oct 19 '17 at 12:06
  • You may be better off using SSE intrinsics instead of Standard C++ code. These intrinsics aren't as portable as C++, but you cannot get the bitwise identical results in portable C++ anyway. – MSalters Oct 19 '17 at 13:15
  • @MSalters: there is no need to use SSE intrinsics. If the compiler emits SSE for basic operations, then it will generate bitwise identical results among all compilers/platforms. The only corner point is whether the compiler uses FMA or not (and even, more is true, as far as I know. This statement is true for all IEEE754 compilant HWs, supposing that it uses 32-bit/64-bit arithmetic, so this is true for even the FPU, if 64-bit/32-bit internal precision is turned on for double/float) – geza Oct 19 '17 at 17:46
  • @geza. Thanks for the suggestion. I tried crlibm on Linux; it worked fine. But I didn't see an easy way to use it on Windows. It's configuration script is a Linux util. I may get motivated to install mingw. – Tyson Hilmer Oct 23 '17 at 14:26
  • @TysonHilmer: crlibm is a larger library. If you only want sin/cos, and performance doesn't matter too much, then you may be able to find just a sin/cos routine which can be easier to compile. – geza Oct 23 '17 at 14:44
  • We used to use a lookup table for sin/cos for fixed point back when floating point was too slow or unavailable. That should be reproducible. – Retired Ninja Oct 31 '17 at 02:22

1 Answers1

1

Most floating point functions have to perform rounding one way or an other. The C/C++ standard is rather vague on the subject, and IEEE conformance is not strict enough on trigonometric functions. Which means that in practice it is useless to try to squeeze correct rounding out of your compilers default math implementation in a portable way.

For instance, the libm implementation (used by gcc) of sin/cos is written in assembly and the algorithm is different for different architectures and most probably depends on the version of the library.

You therefore have two possibilities:

  • implement your own sin/cos using only floating point operations with exact rounding (fused multiply-accumulate + Taylor series)
  • use a 3rd party library with strong rounding considerations

I personally use the MPFR library as a gold standard when dealing with rounding errors. There will be a runtime cost, although I never tried to benchmark it against libm performance.


Custom Implementation

Note that if you decide to implement it yourself, you need to choose the rounding mode and inform the compiler that it matter to you.

In C++ it is done this way:

#include <cfenv>
#pragma STDC FENV_ACCESS ON
#pragma STDC FP_CONTRACT OFF

int main(int, char**) {
    ...
    if(!std::fesetround(FE_TONEAREST))
        throw std::runtime_error("fesetround failed!");
    ...
}
Nonyme
  • 1,220
  • 1
  • 11
  • 22
  • This answer best addresses my problem; which is (most likely) the sin/cos implementations varying across platforms and builds. If no one posts an answer regarding FPU on x64, I'll select this one. – Tyson Hilmer Oct 30 '17 at 09:44
  • 1
    Note that I would not use MPFR in a game engine. It is designed to be as precise as possible (exact rounding) which is probably too expensive for your purpose. I would go for a custom implementation in your case, but don't forget to set the rounding mode with std::fesetround and the STDC pragma. – Nonyme Oct 31 '17 at 01:21