about summary refs log tree commit diff
path: root/sysdeps/powerpc/powerpc32/fpu/s_llrint.c
diff options
context:
space:
mode:
authorJoseph Myers <joseph@codesourcery.com>2015-10-13 00:52:54 +0000
committerJoseph Myers <joseph@codesourcery.com>2015-10-13 00:52:54 +0000
commite8dab9477f33ab7a67405f4e147cbaa1a4bb443b (patch)
treef9a6edfe0acaaa15b4c8a42daad0ef31510e5994 /sysdeps/powerpc/powerpc32/fpu/s_llrint.c
parent5b1766a0cde255eff9868a9eeb49f66a89842cb8 (diff)
downloadglibc-e8dab9477f33ab7a67405f4e147cbaa1a4bb443b.tar.gz
glibc-e8dab9477f33ab7a67405f4e147cbaa1a4bb443b.tar.xz
glibc-e8dab9477f33ab7a67405f4e147cbaa1a4bb443b.zip
Fix powerpc32 llrint, llrintf bad exceptions (bug 16422).
The versions of llrint and llrintf for older powerpc32 processors
convert the results of __rint / __rintf to long long int, resulting in
spurious exceptions from such casts in certain cases.  This patch
makes glibc work around the problems with the libgcc conversions when
the compiler used to build glibc doesn't use the fctidz instruction
for them.

Tested for powerpc.

	[BZ #16422]
	* sysdeps/powerpc/powerpc32/fpu/configure.ac (libc_cv_ppc_fctidz):
	New configure test.
	* sysdeps/powerpc/powerpc32/fpu/configure: Regenerated.
	* config.h.in [_LIBC] (HAVE_PPC_FCTIDZ): New macro.
	* sysdeps/powerpc/powerpc32/fpu/s_llrint.c: Include <limits.h>,
	<math_private.h> and <stdint.h>.
	(__llrint): Avoid conversions to long long int where those might
	raise spurious exceptions.
	* sysdeps/powerpc/powerpc32/fpu/s_llrintf.c: Include
	<math_private.h> and <stdint.h>.
	(__llrintf): Avoid conversions to long long int where those might
	raise spurious exceptions.
Diffstat (limited to 'sysdeps/powerpc/powerpc32/fpu/s_llrint.c')
-rw-r--r--sysdeps/powerpc/powerpc32/fpu/s_llrint.c31
1 files changed, 30 insertions, 1 deletions
diff --git a/sysdeps/powerpc/powerpc32/fpu/s_llrint.c b/sysdeps/powerpc/powerpc32/fpu/s_llrint.c
index 48af9eb726..bb12272461 100644
--- a/sysdeps/powerpc/powerpc32/fpu/s_llrint.c
+++ b/sysdeps/powerpc/powerpc32/fpu/s_llrint.c
@@ -16,13 +16,42 @@
    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>
 
 long long int
 __llrint (double x)
 {
-  return (long long int) __rint (x);
+  double rx = __rint (x);
+  if (HAVE_PPC_FCTIDZ || rx != x)
+    return (long long int) rx;
+  else
+    {
+      /* Avoid incorrect exceptions from libgcc conversions (as of GCC
+	 5): <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59412>.  */
+      if (fabs (rx) < 0x1p31)
+	return (long long int) (long int) rx;
+      uint64_t i0;
+      EXTRACT_WORDS64 (i0, rx);
+      int exponent = ((i0 >> 52) & 0x7ff) - 0x3ff;
+      if (exponent < 63)
+	{
+	  unsigned long long int mant
+	    = (i0 & ((1ULL << 52) - 1)) | (1ULL << 52);
+	  if (exponent < 52)
+	    mant >>= 52 - exponent;
+	  else
+	    mant <<= exponent - 52;
+	  return (long long int) ((i0 & (1ULL << 63)) != 0 ? -mant : mant);
+	}
+      else if (rx == (double) LLONG_MIN)
+	return LLONG_MIN;
+      else
+	return (long long int) (long int) rx << 32;
+    }
 }
 weak_alias (__llrint, llrint)
 #ifdef NO_LONG_DOUBLE