From 478753ef290789114c84c9c7a037206e4d5443f0 Mon Sep 17 00:00:00 2001
From: Sebastian Lackner <sebastian@fds-team.de>
Date: Fri, 10 Apr 2015 07:51:16 +0200
Subject: [PATCH] msvcrt: Calculate sinh/cosh/exp/pow with higher precision.
 (v2)

Based on a patch by Zheng Chen.
---
 dlls/msvcrt/math.c | 61 +++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 58 insertions(+), 3 deletions(-)

diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c
index 59b6a984878..11ba649a947 100644
--- a/dlls/msvcrt/math.c
+++ b/dlls/msvcrt/math.c
@@ -69,6 +69,61 @@ static MSVCRT_matherr_func MSVCRT_default_matherr_func = NULL;
 static BOOL sse2_supported;
 static BOOL sse2_enabled;
 
+#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+
+static inline double precise_cosh( double x )
+{
+    WORD precise_cw = 0x37f, pre_cw;
+    double z;
+    __asm__ __volatile__( "fnstcw %0" : "=m" (pre_cw) );
+    __asm__ __volatile__( "fldcw %0" : : "m" (precise_cw) );
+    z = cosh( x );
+    __asm__ __volatile__( "fldcw %0" : : "m" (pre_cw) );
+    return z;
+}
+
+static inline double precise_exp( double x )
+{
+    WORD precise_cw = 0x37f, pre_cw;
+    double z;
+    __asm__ __volatile__( "fnstcw %0" : "=m" (pre_cw) );
+    __asm__ __volatile__( "fldcw %0" : : "m" (precise_cw) );
+    z = exp( x );
+    __asm__ __volatile__( "fldcw %0" : : "m" (pre_cw) );
+    return z;
+}
+
+static inline double precise_pow( double x, double y )
+{
+    WORD precise_cw = 0x37f, pre_cw;
+    double z;
+    __asm__ __volatile__( "fnstcw %0" : "=m" (pre_cw) );
+    __asm__ __volatile__( "fldcw %0" : : "m" (precise_cw) );
+    z = pow( x, y );
+    __asm__ __volatile__( "fldcw %0" : : "m" (pre_cw) );
+    return z;
+}
+
+static inline double precise_sinh( double x )
+{
+    WORD precise_cw = 0x37f, pre_cw;
+    double z;
+    __asm__ __volatile__( "fnstcw %0" : "=m" (pre_cw) );
+    __asm__ __volatile__( "fldcw %0" : : "m" (precise_cw) );
+    z = sinh( x );
+    __asm__ __volatile__( "fldcw %0" : : "m" (pre_cw) );
+    return z;
+}
+
+#else
+
+#define precise_cosh cosh
+#define precise_exp  exp
+#define precise_pow  pow
+#define precise_sinh sinh
+
+#endif
+
 void msvcrt_init_math(void)
 {
     sse2_supported = sse2_enabled = IsProcessorFeaturePresent( PF_XMMI64_INSTRUCTIONS_AVAILABLE );
@@ -557,7 +612,7 @@ double CDECL MSVCRT_cos( double x )
  */
 double CDECL MSVCRT_cosh( double x )
 {
-  double ret = cosh(x);
+  double ret = precise_cosh(x);
   if (isnan(x)) return math_error(_DOMAIN, "cosh", x, 0, ret);
   return ret;
 }
@@ -567,7 +622,7 @@ double CDECL MSVCRT_cosh( double x )
  */
 double CDECL MSVCRT_exp( double x )
 {
-  double ret = exp(x);
+  double ret = precise_exp(x);
   if (isnan(x)) return math_error(_DOMAIN, "exp", x, 0, ret);
   if (isfinite(x) && !ret) return math_error(_UNDERFLOW, "exp", x, 0, ret);
   if (isfinite(x) && !isfinite(ret)) return math_error(_OVERFLOW, "exp", x, 0, ret);
@@ -638,7 +693,7 @@ double CDECL MSVCRT_sin( double x )
  */
 double CDECL MSVCRT_sinh( double x )
 {
-  double ret = sinh(x);
+  double ret = precise_sinh(x);
   if (isnan(x)) return math_error(_DOMAIN, "sinh", x, 0, ret);
   return ret;
 }
-- 
2.27.0

