about summary refs log tree commit diff
path: root/sysdeps/powerpc/powerpc32/fpu/s_llround.c
diff options
context:
space:
mode:
Diffstat (limited to 'sysdeps/powerpc/powerpc32/fpu/s_llround.c')
-rw-r--r--sysdeps/powerpc/powerpc32/fpu/s_llround.c51
1 files changed, 49 insertions, 2 deletions
diff --git a/sysdeps/powerpc/powerpc32/fpu/s_llround.c b/sysdeps/powerpc/powerpc32/fpu/s_llround.c
index 14d10e1e63..989d9b5763 100644
--- a/sysdeps/powerpc/powerpc32/fpu/s_llround.c
+++ b/sysdeps/powerpc/powerpc32/fpu/s_llround.c
@@ -18,10 +18,10 @@
 
 #include <limits.h>
 #include <math.h>
-#include <math_ldbl_opt.h>
 #include <math_private.h>
 #include <stdint.h>
 #include <libm-alias-double.h>
+#include <math-barriers.h>
 
 /* Round to the nearest integer, with values exactly on a 0.5 boundary
    rounded away from zero, regardless of the current rounding mode.
@@ -31,9 +31,53 @@
 long long int
 __llround (double x)
 {
+#ifdef _ARCH_PWR5X
+  x = round (x);
+  /* The barrier prevents compiler from optimizing it to llround when
+     compiled with -fno-math-errno */
+  math_opt_barrier (x);
+  return x;
+#else
   long long xr;
   if (HAVE_PPC_FCTIDZ)
-    xr = (long long) x;
+    {
+       /* IEEE 1003.1 lround function.  IEEE specifies "round to the nearest
+	  integer value, rounding halfway cases away from zero, regardless of
+	  the current rounding mode."  However PowerPC Architecture defines
+	  "round to Nearest" as "Choose the best approximation. In case of a
+	  tie, choose the one that is even (least significant bit o).".
+	  So we can't use the PowerPC "round to Nearest" mode. Instead we set
+	  "round toward Zero" mode and round by adding +-0.5 before rounding
+	  to the integer value.
+
+	  It is necessary to detect when x is (+-)0x1.fffffffffffffp-2
+	  because adding +-0.5 in this case will cause an erroneous shift,
+	  carry and round.  We simply return 0 if 0.5 > x > -0.5.  Likewise
+	  if x is and odd number between +-(2^52 and 2^53-1) a shift and
+	  carry will erroneously round if biased with +-0.5.  Therefore if x
+	  is greater/less than +-2^52 we don't need to bias the number with
+	  +-0.5.  */
+      double ax = fabs (x);
+
+      if (ax < 0.5)
+	return 0;
+
+      if (ax < 0x1p+52)
+	{
+	  /* Test whether an integer to avoid spurious "inexact".  */
+	  double t = ax + 0x1p+52;
+	  t = t - 0x1p+52;
+	  if (ax != t)
+	    {
+	      ax = ax + 0.5;
+	      if (x < 0.0)
+		ax = -fabs (ax);
+	      x = ax;
+	    }
+        }
+
+      return x;
+    }
   else
     {
       /* Avoid incorrect exceptions from libgcc conversions (as of GCC
@@ -80,5 +124,8 @@ __llround (double x)
 	xr -= (long long) ((unsigned long long) xr - 1) < 0;
     }
   return xr;
+#endif
 }
+#ifndef __llround
 libm_alias_double (__llround, llround)
+#endif