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

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

diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c
index 7c971d3..0e62215 100644
--- a/dlls/msvcrt/math.c
+++ b/dlls/msvcrt/math.c
@@ -393,8 +393,19 @@ double CDECL MSVCRT_cos( double x )
  */
 double CDECL MSVCRT_cosh( double x )
 {
+#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+  WORD precise_cw = 0x37f, pre_cw;
+  double z;
+  if (!isfinite(x)) *MSVCRT__errno() = MSVCRT_EDOM;
+  __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;
+#else
   if (!isfinite(x)) *MSVCRT__errno() = MSVCRT_EDOM;
   return cosh(x);
+#endif
 }
 
 /*********************************************************************
@@ -402,8 +413,19 @@ double CDECL MSVCRT_cosh( double x )
  */
 double CDECL MSVCRT_exp( double x )
 {
+#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+  WORD precise_cw = 0x37f, pre_cw;
+  double z;
+  if (isnan(x)) *MSVCRT__errno() = MSVCRT_EDOM;
+  __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;
+#else
   if (isnan(x)) *MSVCRT__errno() = MSVCRT_EDOM;
   return exp(x);
+#endif
 }
 
 /*********************************************************************
@@ -441,9 +463,20 @@ double CDECL MSVCRT_log10( double x )
 double CDECL MSVCRT_pow( double x, double y )
 {
   /* FIXME: If x < 0 and y is not integral, set EDOM */
+#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+  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) );
+  if (!isfinite(z)) *MSVCRT__errno() = MSVCRT_EDOM;
+  return z;
+#else
   double z = pow(x,y);
   if (!isfinite(z)) *MSVCRT__errno() = MSVCRT_EDOM;
   return z;
+#endif
 }
 
 /*********************************************************************
@@ -460,8 +493,19 @@ double CDECL MSVCRT_sin( double x )
  */
 double CDECL MSVCRT_sinh( double x )
 {
+#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+  WORD precise_cw = 0x37f, pre_cw;
+  double z;
+  if (!isfinite(x)) *MSVCRT__errno() = MSVCRT_EDOM;
+  __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
   if (!isfinite(x)) *MSVCRT__errno() = MSVCRT_EDOM;
   return sinh(x);
+#endif
 }
 
 /*********************************************************************
-- 
2.3.7

