This is the mail archive of the libc-alpha@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: Ping Re: RFC: fixing ldbl-128ibm round-to-integer functions for non-default rounding modes


On Thu, 21 Jan 2016, Joseph Myers wrote:

> Any comments on this 
> <https://sourceware.org/ml/libc-alpha/2015-12/msg00157.html> (for 2.24)?

This is the patch fixing all of ceill, floorl, truncl and roundl that I 
plan to apply for 2.24 (split by function and with appropriate ChangeLog 
entries and bugs filed for ceill, truncl and roundl), given the lack of 
comments.

diff --git a/sysdeps/ieee754/ldbl-128ibm/math_ldbl.h b/sysdeps/ieee754/ldbl-128ibm/math_ldbl.h
index 051352f..625ce00 100644
--- a/sysdeps/ieee754/ldbl-128ibm/math_ldbl.h
+++ b/sysdeps/ieee754/ldbl-128ibm/math_ldbl.h
@@ -230,3 +230,36 @@ ldbl_nearbyint (double a)
     }
   return a;
 }
+
+/* Canonicalize a result from an integer rounding function, in any
+   rounding mode.  *A and *AA are finite and integers, with *A being
+   nonzero; if the result is not already canonical, *AA is plus or
+   minus a power of 2 that does not exceed the least set bit in
+   *A.  */
+static inline void
+ldbl_canonicalize_int (double *a, double *aa)
+{
+  int64_t ax, aax;
+  EXTRACT_WORDS64 (ax, *a);
+  EXTRACT_WORDS64 (aax, *aa);
+  int expdiff = ((ax >> 52) & 0x7ff) - ((aax >> 52) & 0x7ff);
+  if (expdiff <= 53)
+    {
+      if (expdiff == 53)
+	{
+	  /* Half way between two double values; noncanonical iff the
+	     low bit of A's mantissa is 1.  */
+	  if ((ax & 1) != 0)
+	    {
+	      *a += 2 * *aa;
+	      *aa = -*aa;
+	    }
+	}
+      else
+	{
+	  /* The sum can be represented in a single double.  */
+	  *a += *aa;
+	  *aa = 0;
+	}
+    }
+}
diff --git a/sysdeps/ieee754/ldbl-128ibm/s_ceill.c b/sysdeps/ieee754/ldbl-128ibm/s_ceill.c
index ac649b7..635fddc 100644
--- a/sysdeps/ieee754/ldbl-128ibm/s_ceill.c
+++ b/sysdeps/ieee754/ldbl-128ibm/s_ceill.c
@@ -35,42 +35,22 @@ __ceill (long double x)
 			&& __builtin_isless (__builtin_fabs (xh),
 					     __builtin_inf ()), 1))
     {
-      double orig_xh;
-
-      /* Long double arithmetic, including the canonicalisation below,
-	 only works in round-to-nearest mode.  */
-
-      /* Convert the high double to integer.  */
-      orig_xh = xh;
-      hi = ldbl_nearbyint (xh);
-
-      /* Subtract integral high part from the value.  */
-      xh -= hi;
-      ldbl_canonicalize (&xh, &xl);
-
-      /* Now convert the low double, adjusted for any remainder from the
-         high double.  */
-      lo = ldbl_nearbyint (xh);
-
-      /* Adjust the result when the remainder is non-zero.  nearbyint
-         rounds values to the nearest integer, and values halfway
-         between integers to the nearest even integer.  ceill must
-         round towards +Inf.  */
-      xh -= lo;
-      ldbl_canonicalize (&xh, &xl);
-
-      if (xh > 0.0 || (xh == 0.0 && xl > 0.0))
-	lo += 1.0;
-
-      /* Ensure the final value is canonical.  In certain cases,
-         rounding causes hi,lo calculated so far to be non-canonical.  */
-      xh = hi;
-      xl = lo;
-      ldbl_canonicalize (&xh, &xl);
-
-      /* Ensure we return -0 rather than +0 when appropriate.  */
-      if (orig_xh < 0.0)
-	xh = -__builtin_fabs (xh);
+      hi = __ceil (xh);
+      if (hi != xh)
+	{
+	  /* The high part is not an integer; the low part does not
+	     affect the result.  */
+	  xh = hi;
+	  xl = 0;
+	}
+      else
+	{
+	  /* The high part is a nonzero integer.  */
+	  lo = __ceil (xl);
+	  xh = hi;
+	  xl = lo;
+	  ldbl_canonicalize_int (&xh, &xl);
+	}
     }
 
   return ldbl_pack (xh, xl);
diff --git a/sysdeps/ieee754/ldbl-128ibm/s_floorl.c b/sysdeps/ieee754/ldbl-128ibm/s_floorl.c
index 9122308..a146964 100644
--- a/sysdeps/ieee754/ldbl-128ibm/s_floorl.c
+++ b/sysdeps/ieee754/ldbl-128ibm/s_floorl.c
@@ -35,35 +35,22 @@ __floorl (long double x)
 			&& __builtin_isless (__builtin_fabs (xh),
 					     __builtin_inf ()), 1))
     {
-      /* Long double arithmetic, including the canonicalisation below,
-	 only works in round-to-nearest mode.  */
-
-      /* Convert the high double to integer.  */
-      hi = ldbl_nearbyint (xh);
-
-      /* Subtract integral high part from the value.  */
-      xh -= hi;
-      ldbl_canonicalize (&xh, &xl);
-
-      /* Now convert the low double, adjusted for any remainder from the
-         high double.  */
-      lo = ldbl_nearbyint (xh);
-
-      /* Adjust the result when the remainder is non-zero.  nearbyint
-         rounds values to the nearest integer, and values halfway
-         between integers to the nearest even integer.  floorl must
-         round towards -Inf.  */
-      xh -= lo;
-      ldbl_canonicalize (&xh, &xl);
-
-      if (xh < 0.0 || (xh == 0.0 && xl < 0.0))
-	lo += -1.0;
-
-      /* Ensure the final value is canonical.  In certain cases,
-         rounding causes hi,lo calculated so far to be non-canonical.  */
-      xh = hi;
-      xl = lo;
-      ldbl_canonicalize (&xh, &xl);
+      hi = __floor (xh);
+      if (hi != xh)
+	{
+	  /* The high part is not an integer; the low part does not
+	     affect the result.  */
+	  xh = hi;
+	  xl = 0;
+	}
+      else
+	{
+	  /* The high part is a nonzero integer.  */
+	  lo = __floor (xl);
+	  xh = hi;
+	  xl = lo;
+	  ldbl_canonicalize_int (&xh, &xl);
+	}
     }
 
   return ldbl_pack (xh, xl);
diff --git a/sysdeps/ieee754/ldbl-128ibm/s_roundl.c b/sysdeps/ieee754/ldbl-128ibm/s_roundl.c
index 20813ed..b01510f 100644
--- a/sysdeps/ieee754/ldbl-128ibm/s_roundl.c
+++ b/sysdeps/ieee754/ldbl-128ibm/s_roundl.c
@@ -38,46 +38,44 @@ __roundl (long double x)
 			&& __builtin_isless (__builtin_fabs (xh),
 					     __builtin_inf ()), 1))
     {
-      double orig_xh;
-
-      /* Long double arithmetic, including the canonicalisation below,
-	 only works in round-to-nearest mode.  */
-
-      /* Convert the high double to integer.  */
-      orig_xh = xh;
-      hi = ldbl_nearbyint (xh);
-
-      /* Subtract integral high part from the value.  */
-      xh -= hi;
-      ldbl_canonicalize (&xh, &xl);
-
-      /* Now convert the low double, adjusted for any remainder from the
-	 high double.  */
-      lo = ldbl_nearbyint (xh);
-
-      /* Adjust the result when the remainder is exactly 0.5.  nearbyint
-	 rounds values halfway between integers to the nearest even
-	 integer.  roundl must round away from zero.
-	 Also correct cases where nearbyint returns an incorrect value
-	 for LO.  */
-      xh -= lo;
-      ldbl_canonicalize (&xh, &xl);
-      if (xh == 0.5)
+      hi = __round (xh);
+      if (hi != xh)
 	{
-	  if (xl > 0.0 || (xl == 0.0 && orig_xh > 0.0))
-	    lo += 1.0;
+	  /* The high part is not an integer; the low part only
+	     affects the result if the high part is exactly half way
+	     between two integers and the low part is nonzero with the
+	     opposite sign.  */
+	  if (fabs (hi - xh) == 0.5)
+	    {
+	      if (xh > 0 && xl < 0)
+		xh = hi - 1;
+	      else if (xh < 0 && xl > 0)
+		xh = hi + 1;
+	      else
+		xh = hi;
+	    }
+	  else
+	    xh = hi;
+	  xl = 0;
 	}
-      else if (-xh == 0.5)
+      else
 	{
-	  if (xl < 0.0 || (xl == 0.0 && orig_xh < 0.0))
-	    lo -= 1.0;
+	  /* The high part is a nonzero integer.  */
+	  lo = __round (xl);
+	  if (fabs (lo - xl) == 0.5)
+	    {
+	      if (xh > 0 && xl < 0)
+		xl = lo + 1;
+	      else if (xh < 0 && lo > 0)
+		xl = lo - 1;
+	      else
+		xl = lo;
+	    }
+	  else
+	    xl = lo;
+	  xh = hi;
+	  ldbl_canonicalize_int (&xh, &xl);
 	}
-
-      /* Ensure the final value is canonical.  In certain cases,
-	 rounding causes hi,lo calculated so far to be non-canonical.  */
-      xh = hi;
-      xl = lo;
-      ldbl_canonicalize (&xh, &xl);
     }
 
   return ldbl_pack (xh, xl);
diff --git a/sysdeps/ieee754/ldbl-128ibm/s_truncl.c b/sysdeps/ieee754/ldbl-128ibm/s_truncl.c
index df58b64..b7d4bb5 100644
--- a/sysdeps/ieee754/ldbl-128ibm/s_truncl.c
+++ b/sysdeps/ieee754/ldbl-128ibm/s_truncl.c
@@ -35,50 +35,22 @@ __truncl (long double x)
 			&& __builtin_isless (__builtin_fabs (xh),
 					     __builtin_inf ()), 1))
     {
-      double orig_xh;
-
-      /* Long double arithmetic, including the canonicalisation below,
-	 only works in round-to-nearest mode.  */
-
-      /* Convert the high double to integer.  */
-      orig_xh = xh;
-      hi = ldbl_nearbyint (xh);
-
-      /* Subtract integral high part from the value.  */
-      xh -= hi;
-      ldbl_canonicalize (&xh, &xl);
-
-      /* Now convert the low double, adjusted for any remainder from the
-         high double.  */
-      lo = ldbl_nearbyint (xh);
-
-      /* Adjust the result when the remainder is non-zero.  nearbyint
-         rounds values to the nearest integer, and values halfway
-         between integers to the nearest even integer.  floorl must
-         round towards -Inf.  */
-      xh -= lo;
-      ldbl_canonicalize (&xh, &xl);
-
-      if (orig_xh < 0.0)
+      hi = __trunc (xh);
+      if (hi != xh)
 	{
-	  if (xh > 0.0 || (xh == 0.0 && xl > 0.0))
-	    lo += 1.0;
+	  /* The high part is not an integer; the low part does not
+	     affect the result.  */
+	  xh = hi;
+	  xl = 0;
 	}
       else
 	{
-	  if (xh < 0.0 || (xh == 0.0 && xl < 0.0))
-	    lo -= 1.0;
+	  /* The high part is a nonzero integer.  */
+	  lo = xh > 0 ? __floor (xl) : __ceil (xl);
+	  xh = hi;
+	  xl = lo;
+	  ldbl_canonicalize_int (&xh, &xl);
 	}
-
-      /* Ensure the final value is canonical.  In certain cases,
-         rounding causes hi,lo calculated so far to be non-canonical.  */
-      xh = hi;
-      xl = lo;
-      ldbl_canonicalize (&xh, &xl);
-
-      /* Ensure we return -0 rather than +0 when appropriate.  */
-      if (orig_xh < 0.0)
-	xh = -__builtin_fabs (xh);
     }
 
   return ldbl_pack (xh, xl);

-- 
Joseph S. Myers
joseph@codesourcery.com


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]