diff mbox

Avoid "inexact" exceptions in i386/x86_64 ceil functions (bug 15479)

Message ID alpine.DEB.2.20.1606221618340.29819@digraph.polyomino.org.uk
State Committed
Headers show

Commit Message

Joseph Myers June 22, 2016, 4:23 p.m. UTC
As discussed in
<https://sourceware.org/ml/libc-alpha/2016-05/msg00577.html>, TS
18661-1 disallows ceil, floor, round and trunc functions from raising
the "inexact" exception, in accordance with general IEEE 754 semantics
for when that exception is raised.  Fixing this for x87 floating point
is more complicated than for the other versions of these functions,
because they use the frndint instruction that raises "inexact" and
this can only be avoided by saving and restoring the whole
floating-point environment.

As I noted in
<https://sourceware.org/ml/libc-alpha/2016-06/msg00128.html>, I have
now implemented a GCC option -fno-fp-int-builtin-inexact for GCC 7,
such that GCC will inline these functions on x86, without caring about
"inexact", when the default -ffp-int-builtin-inexact is in effect.
This allows users to get optimized code depending on the options they
pass to the compiler, while making the out-of-line functions follow TS
18661-1 semantics and avoid "inexact".

This patch duly fixes the out-of-line ceil function implementations to
avoid "inexact", in the same way as the nearbyint implementations.

I do not know how the performance of implementations such as these
based on saving the environment and changing the rounding mode
temporarily compares to that of the C versions or SSE 4.1 versions (of
course, for 32-bit x86 SSE implementations still need to get the
return value in an x87 register); it's entirely possible other
implementations could be faster in some cases.

Tested for x86_64 and x86.  I intend to commit this, and corresponding 
floor and trunc fixes, in the absence of further comments.

2016-06-22  Joseph Myers  <joseph@codesourcery.com>

	[BZ #15479]
	* sysdeps/i386/fpu/s_ceil.S (__ceil): Save and restore
	floating-point environment rather than just control word.
	* sysdeps/i386/fpu/s_ceilf.S (__ceilf): Likewise.
	* sysdeps/i386/fpu/s_ceill.S (__ceill): Save and restore
	floating-point environment, with "invalid" exceptions merged in,
	rather than just control word.
	* sysdeps/x86_64/fpu/s_ceill.S (__ceill): Likewise.
	* math/libm-test.inc (ceil_test_data): Do not allow spurious
	"inexact" exceptions.
diff mbox

Patch

diff --git a/math/libm-test.inc b/math/libm-test.inc
index 17d65b4..50b1976 100644
--- a/math/libm-test.inc
+++ b/math/libm-test.inc
@@ -6075,14 +6075,13 @@  static const struct test_f_f_data ceil_test_data[] =
     TEST_f_f (ceil, snan_value, qnan_value, NO_INEXACT_EXCEPTION|INVALID_EXCEPTION),
     TEST_f_f (ceil, -snan_value, qnan_value, NO_INEXACT_EXCEPTION|INVALID_EXCEPTION),
 
-    /* Bug 15479: spurious "inexact" exception may occur.  */
-    TEST_f_f (ceil, lit_pi, 4.0, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, -lit_pi, -3.0, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, min_subnorm_value, 1.0, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, min_value, 1.0, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, 0.1, 1.0, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, 0.25, 1.0, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, 0.625, 1.0, ERRNO_UNCHANGED),
+    TEST_f_f (ceil, lit_pi, 4.0, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, -lit_pi, -3.0, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, min_subnorm_value, 1.0, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, min_value, 1.0, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, 0.1, 1.0, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, 0.25, 1.0, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, 0.625, 1.0, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
     TEST_f_f (ceil, 1, 1, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
     TEST_f_f (ceil, 2, 2, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
     TEST_f_f (ceil, 0x1p23, 0x1p23, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
@@ -6101,11 +6100,11 @@  static const struct test_f_f_data ceil_test_data[] =
     TEST_f_f (ceil, 0x1p113, 0x1p113, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
     TEST_f_f (ceil, 0x1p114, 0x1p114, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
     TEST_f_f (ceil, max_value, max_value, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
-    TEST_f_f (ceil, -min_subnorm_value, minus_zero, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, -min_value, minus_zero, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, -0.1, minus_zero, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, -0.25, minus_zero, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, -0.625, minus_zero, ERRNO_UNCHANGED),
+    TEST_f_f (ceil, -min_subnorm_value, minus_zero, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, -min_value, minus_zero, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, -0.1, minus_zero, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, -0.25, minus_zero, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, -0.625, minus_zero, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
     TEST_f_f (ceil, -1, -1, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
     TEST_f_f (ceil, -2, -2, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
     TEST_f_f (ceil, -0x1p23, -0x1p23, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
@@ -6127,81 +6126,81 @@  static const struct test_f_f_data ceil_test_data[] =
 
 #if MANT_DIG >= 64
     /* The result can only be represented in long double.  */
-    TEST_f_f (ceil, 4503599627370495.5L, 4503599627370496.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, 4503599627370496.25L, 4503599627370497.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, 4503599627370496.5L, 4503599627370497.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, 4503599627370496.75L, 4503599627370497.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, 4503599627370497.5L, 4503599627370498.0L, ERRNO_UNCHANGED),
-
-    TEST_f_f (ceil, -4503599627370495.5L, -4503599627370495.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, -4503599627370496.25L, -4503599627370496.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, -4503599627370496.5L, -4503599627370496.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, -4503599627370496.75L, -4503599627370496.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, -4503599627370497.5L, -4503599627370497.0L, ERRNO_UNCHANGED),
+    TEST_f_f (ceil, 4503599627370495.5L, 4503599627370496.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, 4503599627370496.25L, 4503599627370497.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, 4503599627370496.5L, 4503599627370497.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, 4503599627370496.75L, 4503599627370497.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, 4503599627370497.5L, 4503599627370498.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+
+    TEST_f_f (ceil, -4503599627370495.5L, -4503599627370495.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, -4503599627370496.25L, -4503599627370496.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, -4503599627370496.5L, -4503599627370496.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, -4503599627370496.75L, -4503599627370496.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, -4503599627370497.5L, -4503599627370497.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
 
 # if MANT_DIG > 100
-    TEST_f_f (ceil, 4503599627370494.5000000000001L, 4503599627370495.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, 4503599627370495.5000000000001L, 4503599627370496.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, 4503599627370496.5000000000001L, 4503599627370497.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, -4503599627370494.5000000000001L, -4503599627370494.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, -4503599627370495.5000000000001L, -4503599627370495.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, -4503599627370496.5000000000001L, -4503599627370496.0L, ERRNO_UNCHANGED),
+    TEST_f_f (ceil, 4503599627370494.5000000000001L, 4503599627370495.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, 4503599627370495.5000000000001L, 4503599627370496.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, 4503599627370496.5000000000001L, 4503599627370497.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, -4503599627370494.5000000000001L, -4503599627370494.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, -4503599627370495.5000000000001L, -4503599627370495.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, -4503599627370496.5000000000001L, -4503599627370496.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
 # endif
 
-    TEST_f_f (ceil, 9007199254740991.5L, 9007199254740992.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, 9007199254740992.25L, 9007199254740993.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, 9007199254740992.5L, 9007199254740993.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, 9007199254740992.75L, 9007199254740993.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, 9007199254740993.5L, 9007199254740994.0L, ERRNO_UNCHANGED),
+    TEST_f_f (ceil, 9007199254740991.5L, 9007199254740992.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, 9007199254740992.25L, 9007199254740993.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, 9007199254740992.5L, 9007199254740993.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, 9007199254740992.75L, 9007199254740993.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, 9007199254740993.5L, 9007199254740994.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
 
-    TEST_f_f (ceil, -9007199254740991.5L, -9007199254740991.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, -9007199254740992.25L, -9007199254740992.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, -9007199254740992.5L, -9007199254740992.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, -9007199254740992.75L, -9007199254740992.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, -9007199254740993.5L, -9007199254740993.0L, ERRNO_UNCHANGED),
+    TEST_f_f (ceil, -9007199254740991.5L, -9007199254740991.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, -9007199254740992.25L, -9007199254740992.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, -9007199254740992.5L, -9007199254740992.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, -9007199254740992.75L, -9007199254740992.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, -9007199254740993.5L, -9007199254740993.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
 
 # if MANT_DIG > 100
-    TEST_f_f (ceil, 9007199254740991.0000000000001L, 9007199254740992.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, 9007199254740992.0000000000001L, 9007199254740993.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, 9007199254740993.0000000000001L, 9007199254740994.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, 9007199254740991.5000000000001L, 9007199254740992.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, 9007199254740992.5000000000001L, 9007199254740993.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, 9007199254740993.5000000000001L, 9007199254740994.0L, ERRNO_UNCHANGED),
-
-    TEST_f_f (ceil, -9007199254740991.0000000000001L, -9007199254740991.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, -9007199254740992.0000000000001L, -9007199254740992.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, -9007199254740993.0000000000001L, -9007199254740993.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, -9007199254740991.5000000000001L, -9007199254740991.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, -9007199254740992.5000000000001L, -9007199254740992.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, -9007199254740993.5000000000001L, -9007199254740993.0L, ERRNO_UNCHANGED),
+    TEST_f_f (ceil, 9007199254740991.0000000000001L, 9007199254740992.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, 9007199254740992.0000000000001L, 9007199254740993.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, 9007199254740993.0000000000001L, 9007199254740994.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, 9007199254740991.5000000000001L, 9007199254740992.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, 9007199254740992.5000000000001L, 9007199254740993.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, 9007199254740993.5000000000001L, 9007199254740994.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+
+    TEST_f_f (ceil, -9007199254740991.0000000000001L, -9007199254740991.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, -9007199254740992.0000000000001L, -9007199254740992.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, -9007199254740993.0000000000001L, -9007199254740993.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, -9007199254740991.5000000000001L, -9007199254740991.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, -9007199254740992.5000000000001L, -9007199254740992.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, -9007199254740993.5000000000001L, -9007199254740993.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
 # endif
 
-    TEST_f_f (ceil, 72057594037927935.5L, 72057594037927936.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, 72057594037927936.25L, 72057594037927937.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, 72057594037927936.5L, 72057594037927937.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, 72057594037927936.75L, 72057594037927937.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, 72057594037927937.5L, 72057594037927938.0L, ERRNO_UNCHANGED),
+    TEST_f_f (ceil, 72057594037927935.5L, 72057594037927936.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, 72057594037927936.25L, 72057594037927937.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, 72057594037927936.5L, 72057594037927937.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, 72057594037927936.75L, 72057594037927937.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, 72057594037927937.5L, 72057594037927938.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
 
-    TEST_f_f (ceil, -72057594037927935.5L, -72057594037927935.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, -72057594037927936.25L, -72057594037927936.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, -72057594037927936.5L, -72057594037927936.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, -72057594037927936.75L, -72057594037927936.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, -72057594037927937.5L, -72057594037927937.0L, ERRNO_UNCHANGED),
+    TEST_f_f (ceil, -72057594037927935.5L, -72057594037927935.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, -72057594037927936.25L, -72057594037927936.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, -72057594037927936.5L, -72057594037927936.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, -72057594037927936.75L, -72057594037927936.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, -72057594037927937.5L, -72057594037927937.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
 
     /* Check cases where first double is a exact integer higher than 2^52 and
        the precision is determined by second long double for IBM long double.  */
-    TEST_f_f (ceil,  34503599627370498.515625L, 34503599627370499.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, -34503599627370498.515625L, -34503599627370498.0L, ERRNO_UNCHANGED),
+    TEST_f_f (ceil,  34503599627370498.515625L, 34503599627370499.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, -34503599627370498.515625L, -34503599627370498.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
 # if MANT_DIG >= 106
-    TEST_f_f (ceil,  1192568192774434123539907640624.484375L, 1192568192774434123539907640625.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, -1192568192774434123539907640624.484375L, -1192568192774434123539907640624.0L, ERRNO_UNCHANGED),
+    TEST_f_f (ceil,  1192568192774434123539907640624.484375L, 1192568192774434123539907640625.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, -1192568192774434123539907640624.484375L, -1192568192774434123539907640624.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
 # endif
 
-    TEST_f_f (ceil, 10141204801825835211973625643007.5L, 10141204801825835211973625643008.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, 10141204801825835211973625643008.25L, 10141204801825835211973625643009.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, 10141204801825835211973625643008.5L, 10141204801825835211973625643009.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, 10141204801825835211973625643008.75L, 10141204801825835211973625643009.0L, ERRNO_UNCHANGED),
-    TEST_f_f (ceil, 10141204801825835211973625643009.5L, 10141204801825835211973625643010.0L, ERRNO_UNCHANGED),
+    TEST_f_f (ceil, 10141204801825835211973625643007.5L, 10141204801825835211973625643008.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, 10141204801825835211973625643008.25L, 10141204801825835211973625643009.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, 10141204801825835211973625643008.5L, 10141204801825835211973625643009.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, 10141204801825835211973625643008.75L, 10141204801825835211973625643009.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+    TEST_f_f (ceil, 10141204801825835211973625643009.5L, 10141204801825835211973625643010.0L, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
 #endif
   };
 
diff --git a/sysdeps/i386/fpu/s_ceil.S b/sysdeps/i386/fpu/s_ceil.S
index f32fa26..1226bb2 100644
--- a/sysdeps/i386/fpu/s_ceil.S
+++ b/sysdeps/i386/fpu/s_ceil.S
@@ -9,10 +9,10 @@  RCSID("$NetBSD: s_ceil.S,v 1.4 1995/05/08 23:52:13 jtc Exp $")
 
 ENTRY(__ceil)
 	fldl	4(%esp)
-	subl	$8,%esp
-	cfi_adjust_cfa_offset (8)
+	subl	$32,%esp
+	cfi_adjust_cfa_offset (32)
 
-	fstcw	4(%esp)			/* store fpu control word */
+	fnstenv	4(%esp)			/* store fpu environment */
 
 	/* We use here %edx although only the low 1 bits are defined.
 	   But none of the operations should care and they are faster
@@ -25,10 +25,10 @@  ENTRY(__ceil)
 
 	frndint				/* round */
 
-	fldcw	4(%esp)			/* restore original control word */
+	fldenv	4(%esp)			/* restore original environment */
 
-	addl	$8,%esp
-	cfi_adjust_cfa_offset (-8)
+	addl	$32,%esp
+	cfi_adjust_cfa_offset (-32)
 	ret
 END (__ceil)
 weak_alias (__ceil, ceil)
diff --git a/sysdeps/i386/fpu/s_ceilf.S b/sysdeps/i386/fpu/s_ceilf.S
index 4fe703b..d345c09 100644
--- a/sysdeps/i386/fpu/s_ceilf.S
+++ b/sysdeps/i386/fpu/s_ceilf.S
@@ -9,10 +9,10 @@  RCSID("$NetBSD: s_ceilf.S,v 1.3 1995/05/08 23:52:44 jtc Exp $")
 
 ENTRY(__ceilf)
 	flds	4(%esp)
-	subl	$8,%esp
-	cfi_adjust_cfa_offset (8)
+	subl	$32,%esp
+	cfi_adjust_cfa_offset (32)
 
-	fstcw	4(%esp)			/* store fpu control word */
+	fnstenv	4(%esp)			/* store fpu environment */
 
 	/* We use here %edx although only the low 1 bits are defined.
 	   But none of the operations should care and they are faster
@@ -25,10 +25,10 @@  ENTRY(__ceilf)
 
 	frndint				/* round */
 
-	fldcw	4(%esp)			/* restore original control word */
+	fldenv	4(%esp)			/* restore original environment */
 
-	addl	$8,%esp
-	cfi_adjust_cfa_offset (-8)
+	addl	$32,%esp
+	cfi_adjust_cfa_offset (-32)
 	ret
 END (__ceilf)
 weak_alias (__ceilf, ceilf)
diff --git a/sysdeps/i386/fpu/s_ceill.S b/sysdeps/i386/fpu/s_ceill.S
index 4b272c5..7c08f43 100644
--- a/sysdeps/i386/fpu/s_ceill.S
+++ b/sysdeps/i386/fpu/s_ceill.S
@@ -10,10 +10,10 @@  RCSID("$NetBSD: $")
 
 ENTRY(__ceill)
 	fldt	4(%esp)
-	subl	$8,%esp
-	cfi_adjust_cfa_offset (8)
+	subl	$32,%esp
+	cfi_adjust_cfa_offset (32)
 
-	fstcw	4(%esp)			/* store fpu control word */
+	fnstenv	4(%esp)			/* store fpu environment */
 
 	/* We use here %edx although only the low 1 bits are defined.
 	   But none of the operations should care and they are faster
@@ -26,10 +26,15 @@  ENTRY(__ceill)
 
 	frndint				/* round */
 
-	fldcw	4(%esp)			/* restore original control word */
+	/* Preserve "invalid" exceptions from sNaN input.  */
+	fnstsw
+	andl	$0x1, %eax
+	orl	%eax, 8(%esp)
 
-	addl	$8,%esp
-	cfi_adjust_cfa_offset (-8)
+	fldenv	4(%esp)			/* restore original environment */
+
+	addl	$32,%esp
+	cfi_adjust_cfa_offset (-32)
 	ret
 END (__ceill)
 weak_alias (__ceill, ceill)
diff --git a/sysdeps/x86_64/fpu/s_ceill.S b/sysdeps/x86_64/fpu/s_ceill.S
index 910c371..9d8b79d 100644
--- a/sysdeps/x86_64/fpu/s_ceill.S
+++ b/sysdeps/x86_64/fpu/s_ceill.S
@@ -11,20 +11,25 @@ 
 ENTRY(__ceill)
 	fldt	8(%rsp)
 
-	fstcw	-4(%rsp)		/* store fpu control word */
+	fnstenv	-28(%rsp)		/* store fpu environment */
 
 	/* We use here %edx although only the low 1 bits are defined.
 	   But none of the operations should care and they are faster
 	   than the 16 bit operations.  */
 	movl	$0x0800,%edx		/* round towards +oo */
-	orl	-4(%rsp),%edx
+	orl	-28(%rsp),%edx
 	andl	$0xfbff,%edx
-	movl	%edx,-8(%rsp)
-	fldcw	-8(%rsp)		/* load modified control word */
+	movl	%edx,-32(%rsp)
+	fldcw	-32(%rsp)		/* load modified control word */
 
 	frndint				/* round */
 
-	fldcw	-4(%rsp)		/* restore original control word */
+	/* Preserve "invalid" exceptions from sNaN input.  */
+	fnstsw
+	andl	$0x1, %eax
+	orl	%eax, -24(%rsp)
+
+	fldenv	-28(%rsp)		/* restore original environment */
 
 	ret
 END (__ceill)