about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog12
-rw-r--r--NEWS2
-rw-r--r--sysdeps/powerpc/powerpc32/fpu/s_llround.c40
-rw-r--r--sysdeps/powerpc/powerpc32/fpu/s_llroundf.c30
4 files changed, 81 insertions, 3 deletions
diff --git a/ChangeLog b/ChangeLog
index 3334436e78..0a049d246b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+2015-10-13  Joseph Myers  <joseph@codesourcery.com>
+
+	[BZ #19125]
+	* sysdeps/powerpc/powerpc32/fpu/s_llround.c: Include <limits.h>,
+	<math_private.h> and <stdint.h>.
+	(__llround): Avoid conversions to and from long long int, and
+	subtractions, where those might raise spurious exceptions.
+	* sysdeps/powerpc/powerpc32/fpu/s_llroundf.c: Include
+	<math_private.h> and <stdint.h>.
+	(__llroundf): Avoid conversions to and from long long int, and
+	subtractions, where those might raise spurious exceptions.
+
 2015-10-13  H.J. Lu  <hongjiu.lu@intel.com>
 
 	* sysdeps/unix/sysv/linux/adjtime.c (ADJTIME): Use
diff --git a/NEWS b/NEWS
index 7f39543638..6d39148afc 100644
--- a/NEWS
+++ b/NEWS
@@ -19,7 +19,7 @@ Version 2.23
   18870, 18872, 18873, 18875, 18887, 18921, 18951, 18952, 18956, 18961,
   18966, 18967, 18969, 18970, 18977, 18980, 18981, 18985, 19003, 19012,
   19016, 19018, 19032, 19046, 19049, 19050, 19059, 19071, 19076, 19077,
-  19078, 19079, 19085, 19086, 19088, 19094, 19095, 19124
+  19078, 19079, 19085, 19086, 19088, 19094, 19095, 19124, 19125
 
 * The obsolete header <regexp.h> has been removed.  Programs that require
   this header must be updated to use <regex.h> instead.
diff --git a/sysdeps/powerpc/powerpc32/fpu/s_llround.c b/sysdeps/powerpc/powerpc32/fpu/s_llround.c
index 85514a1d3f..bdee74b13c 100644
--- a/sysdeps/powerpc/powerpc32/fpu/s_llround.c
+++ b/sysdeps/powerpc/powerpc32/fpu/s_llround.c
@@ -16,8 +16,11 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
+#include <limits.h>
 #include <math.h>
 #include <math_ldbl_opt.h>
+#include <math_private.h>
+#include <stdint.h>
 
 /* Round to the nearest integer, with values exactly on a 0.5 boundary
    rounded away from zero, regardless of the current rounding mode.
@@ -27,7 +30,42 @@
 long long int
 __llround (double x)
 {
-  long long xr = (long long) x;
+  long long xr;
+  if (HAVE_PPC_FCTIDZ)
+    xr = (long long) x;
+  else
+    {
+      /* Avoid incorrect exceptions from libgcc conversions (as of GCC
+	 5): <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59412>.  */
+      if (fabs (x) < 0x1p31)
+	xr = (long long int) (long int) x;
+      else
+	{
+	  uint64_t i0;
+	  EXTRACT_WORDS64 (i0, x);
+	  int exponent = ((i0 >> 52) & 0x7ff) - 0x3ff;
+	  if (exponent < 63)
+	    {
+	      unsigned long long int mant
+		= (i0 & ((1ULL << 52) - 1)) | (1ULL << 52);
+	      if (exponent < 52)
+		/* llround is not required to raise "inexact".  */
+		mant >>= 52 - exponent;
+	      else
+		mant <<= exponent - 52;
+	      xr = (long long int) ((i0 & (1ULL << 63)) != 0 ? -mant : mant);
+	    }
+	  else if (x == (double) LLONG_MIN)
+	    xr = LLONG_MIN;
+	  else
+	    xr = (long long int) (long int) x << 32;
+	}
+    }
+  /* Avoid spurious "inexact" converting LLONG_MAX to double, and from
+     subtraction when the result is out of range, by returning early
+     for arguments large enough that no rounding is needed.  */
+  if (!(fabs (x) < 0x1p52))
+    return xr;
   double xrf = (double) xr;
 
   if (x >= 0.0)
diff --git a/sysdeps/powerpc/powerpc32/fpu/s_llroundf.c b/sysdeps/powerpc/powerpc32/fpu/s_llroundf.c
index 8ce4b26191..6c67857ec5 100644
--- a/sysdeps/powerpc/powerpc32/fpu/s_llroundf.c
+++ b/sysdeps/powerpc/powerpc32/fpu/s_llroundf.c
@@ -17,6 +17,8 @@
    <http://www.gnu.org/licenses/>.  */
 
 #include <math.h>
+#include <math_private.h>
+#include <stdint.h>
 
 /* Round to the nearest integer, with values exactly on a 0.5 boundary
    rounded away from zero, regardless of the current rounding mode.
@@ -26,7 +28,33 @@
 long long int
 __llroundf (float x)
 {
-  long long xr = (long long) x;
+  long long xr;
+  if (HAVE_PPC_FCTIDZ)
+    xr = (long long) x;
+  else
+    {
+      float ax = fabsf (x);
+      /* Avoid incorrect exceptions from libgcc conversions (as of GCC
+	 5): <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59412>.  */
+      if (ax < 0x1p31f)
+	xr = (long long int) (long int) x;
+      else if (!(ax < 0x1p55f))
+	xr = (long long int) (long int) (x * 0x1p-32f) << 32;
+      else
+	{
+	  uint32_t i0;
+	  GET_FLOAT_WORD (i0, x);
+	  int exponent = ((i0 >> 23) & 0xff) - 0x7f;
+	  unsigned long long int mant = (i0 & 0x7fffff) | 0x800000;
+	  mant <<= exponent - 23;
+	  xr = (long long int) ((i0 & 0x80000000) != 0 ? -mant : mant);
+	}
+    }
+  /* Avoid spurious "inexact" converting LLONG_MAX to float, and from
+     subtraction when the result is out of range, by returning early
+     for arguments large enough that no rounding is needed.  */
+  if (!(fabsf (x) < 0x1p23f))
+    return xr;
   float xrf = (float) xr;
 
   if (x >= 0.0)