public inbox for libc-ports@sourceware.org
 help / color / mirror / Atom feed
* Make strtod respect the rounding mode (bug 14518)
@ 2012-08-29 23:46 Joseph S. Myers
  2012-08-30 19:56 ` Richard Henderson
  0 siblings, 1 reply; 3+ messages in thread
From: Joseph S. Myers @ 2012-08-29 23:46 UTC (permalink / raw)
  To: libc-alpha
  Cc: libc-ports, Andreas Krebbel, Ryan S. Arnold, Richard Henderson,
	Mike Frysinger, Carlos O'Donell

This patch fixes bug 14518, strtod ignoring the rounding mode.

For overflow and underflow (of values small enough to underflow to
zero in round-to-nearest), an appropriate calculation can simply be
used that overflows or underflows in the correct sign, so returning
the appropriate result and raising the appropriate exceptions.  For
other values, where the code rounds explicitly, libc needs to get and
act on the current rounding mode.  Because this code is in libc it
can't simply call fegetround.  Instead, an internal header
rounding-mode.h is added to handle getting the rounding mode and using
it to determine rounding, with bits/rounding-mode.h to provide the
architecture-specific method for getting the rounding mode.  These
headers are designed also to be usable for fixing the failure of
printf to respect the rounding mode (bug 5044).

As suggested in bug 5044, the generic approach for getting the
rounding mode in libc is use of <fpu_control.h>.  The generic code
should work for architectures defining _FPU_GETCW and the relevant
_FPU_RC_* macros, where no special measures are needed to determine
whether hardware floating point is actually available.  Some
architectures need their own bits/rounding-mode.h or changes to
fpu_control.h:

* s390 only has a minimal fpu_control.h.  This patch adds a
  bits/rounding-mode.h for s390, which needs review/testing; s390
  maintainers might wish to expand fpu_control.h so that this
  bits/rounding-mode.h can be removed (I don't know why s390 doesn't
  have the full set of definitions in fpu_control.h).

* This patch has special bits/rounding-mode.h headers for arm
  (possible runtime checks on FPU availability) and powerpc-nofpu
  (getting the software rounding mode), for which I've done spot
  testing to make sure strtod works OK with them.

* This patch does not update the alpha port.  alpha maintainers may
  wish to add a definition of _FPU_GETCW to fpu_control.h, or to add
  bits/rounding-mode.h for alpha (I don't know why its fpu_control.h
  doesn't define _FPU_GETCW when it has the other macros needed).

* This patch does not update the ia64 or hppa ports.  Maintainers may
  wish to add fpu_control.h headers or bits/rounding-mode.h for them
  (I don't know if there are specific reasons they lack
  fpu_control.h).

I believe other libc and ports architectures should be OK with the
generic bits/rounding-mode.h here.

Tested x86 and x86_64, with spot tests for arm and powerpc-nofpu as
noted above.

The powerpc spot testing showed that a fix to the testcase generator
was needed for IBM long double: for certain cases it treated as result
of 0.5ulp above the largest representable double as being exact,
whereas actually such a result overflows (because of the peculiarities
of IBM long double: the high-part double must always be the result of
rounding, to nearest, the sum of the two parts).  I believe there are
still powerpc-specific bugs here - the mpn2ldbl code doesn't properly
handle the case of overflowing to infinity, and the overflow exception
should be raised and errno set to ERANGE in that case - but this
change is sufficient for the test to pass (in line with the testcase
principle of avoiding testing inexact results for IBM long double).

2012-08-29  Joseph Myers  <joseph@codesourcery.com>

	[BZ #14518]
	* include/rounding-mode.h: New file.
	* bits/rounding-mode.h: Likewise.
	* sysdeps/s390/fpu/bits/rounding-mode.h: Likewise.
	* stdlib/strtod_l.c: Include <rounding-mode.h>.
	(MAX_VALUE): New macro.
	(MIN_VALUE): Likewise.
	(overflow_value): New function.
	(underflow_value): Likewise.
	(round_and_return): Use overflow_value and underflow_value to
	determine return values in overflow and underflow cases.  Use
	round_away to determine rounding depending on rounding mode.
	(____STRTOF_INTERNAL): Use overflow_value and underflow_value to
	determine return values in overflow and underflow cases.
	* stdlib/gen-tst-strtod-round.c: Include <assert.h>.
	(round_str): Handle values above the maximum for IBM long double
	as inexact.
	* stdlib/tst-strtod-round.c: Include <fenv.h>.
	(struct test_results): New structure.
	(struct test): Use struct test_results to store expected results
	for all rounding modes.
	(TEST): Include expected results for all rounding modes.
	(tests): Regenerated.
	(test_in_one_mode): New function.
	(do_test): Use test_in_one_mode to compute and check results.
	Check results for all rounding modes.
	* stdlib/Makefile ($(objpfx)tst-strtod-round): Depend on
	$(link-libm).

ports/ChangeLog.arm:
2012-08-29  Joseph Myers  <joseph@codesourcery.com>

	* sysdeps/arm/bits/rounding-mode.h: New file.

ports/ChangeLog.powerpc:
2012-08-29  Joseph Myers  <joseph@codesourcery.com>

	* sysdeps/powerpc/nofpu/bits/rounding-mode.h: New file.

diff --git a/bits/rounding-mode.h b/bits/rounding-mode.h
new file mode 100644
index 0000000..18c6607
--- /dev/null
+++ b/bits/rounding-mode.h
@@ -0,0 +1,80 @@
+/* Determine floating-point rounding mode within libc.  Generic version.
+   Copyright (C) 2012 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef _BITS_ROUNDING_MODE_H
+#define _BITS_ROUNDING_MODE_H	1
+
+#include <fpu_control.h>
+
+/* Return the floating-point rounding mode.  */
+
+static inline rounding_mode_t
+get_rounding_mode (void)
+{
+#if (defined _FPU_RC_DOWN			\
+     || defined _FPU_RC_NEAREST			\
+     || defined _FPU_RC_ZERO			\
+     || defined _FPU_RC_UP)
+  fpu_control_t fc;
+  const fpu_control_t mask = (0
+# ifdef _FPU_RC_DOWN
+			      | _FPU_RC_DOWN
+# endif
+# ifdef _FPU_RC_NEAREST
+			      | _FPU_RC_NEAREST
+# endif
+# ifdef _FPU_RC_ZERO
+			      | _FPU_RC_ZERO
+# endif
+# ifdef _FPU_RC_UP
+			      | _FPU_RC_UP
+# endif
+			      );
+
+  _FPU_GETCW (fc);
+  switch (fc & mask)
+    {
+# ifdef _FPU_RC_DOWN
+    case _FPU_RC_DOWN:
+      return round_downward;
+# endif
+
+# ifdef _FPU_RC_NEAREST
+    case _FPU_RC_NEAREST:
+      return round_tonearest;
+# endif
+
+# ifdef _FPU_RC_ZERO
+    case _FPU_RC_ZERO:
+      return round_towardzero;
+# endif
+
+# ifdef _FPU_RC_UP
+    case _FPU_RC_UP:
+      return round_upward;
+# endif
+
+    default:
+      abort ();
+    }
+#else
+  return round_tonearest;
+#endif
+}
+
+#endif /* bits/rounding-mode.h */
diff --git a/include/rounding-mode.h b/include/rounding-mode.h
new file mode 100644
index 0000000..4a8c1c3
--- /dev/null
+++ b/include/rounding-mode.h
@@ -0,0 +1,71 @@
+/* Handle floating-point rounding mode within libc.
+   Copyright (C) 2012 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef _ROUNDING_MODE_H
+#define _ROUNDING_MODE_H	1
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+/* Internal type for floating-point rounding modes.  */
+typedef enum
+{
+  round_downward,
+  round_tonearest,
+  round_towardzero,
+  round_upward
+} rounding_mode_t;
+
+/* Get the architecture-specific definition of how to determine the
+   rounding mode in libc.  */
+#include <bits/rounding-mode.h>
+
+/* Return true if a number should be rounded away from zero in
+   rounding mode MODE, false otherwise.  NEGATIVE is true if the
+   number is negative, false otherwise.  LAST_DIGIT_ODD is true if the
+   last digit of the truncated value (last bit for binary) is odd,
+   false otherwise.  HALF_BIT is true if the number is at least half
+   way from the truncated value to the next value with the
+   least-significant digit in the same place, false otherwise.
+   MORE_BITS is true if the number is not exactly equal to the
+   truncated value or the half-way value, false otherwise.  */
+
+static inline bool
+round_away (bool negative, bool last_digit_odd, bool half_bit, bool more_bits,
+	    rounding_mode_t mode)
+{
+  switch (mode)
+    {
+    case round_downward:
+      return negative && (half_bit || more_bits);
+
+    case round_tonearest:
+      return half_bit && (last_digit_odd || more_bits);
+
+    case round_towardzero:
+      return false;
+
+    case round_upward:
+      return !negative && (half_bit || more_bits);
+
+    default:
+      abort ();
+    }
+}
+
+#endif /* rounding-mode.h */
diff --git a/ports/sysdeps/arm/bits/rounding-mode.h b/ports/sysdeps/arm/bits/rounding-mode.h
new file mode 100644
index 0000000..8757913
--- /dev/null
+++ b/ports/sysdeps/arm/bits/rounding-mode.h
@@ -0,0 +1,58 @@
+/* Determine floating-point rounding mode within libc.  ARM version.
+   Copyright (C) 2012 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef _ARM_BITS_ROUNDING_MODE_H
+#define _ARM_BITS_ROUNDING_MODE_H	1
+
+#include <arm-features.h>
+#include <fenv.h>
+#include <fpu_control.h>
+
+/* Return the floating-point rounding mode.  */
+
+static inline rounding_mode_t
+get_rounding_mode (void)
+{
+  if (ARM_HAVE_VFP)
+    {
+      fpu_control_t fc;
+
+      _FPU_GETCW (fc);
+      switch (fc & FE_TOWARDZERO)
+	{
+	case FE_DOWNWARD:
+	  return round_downward;
+
+	case FE_TONEAREST:
+	  return round_tonearest;
+
+	case FE_TOWARDZERO:
+	  return round_towardzero;
+
+	case FE_UPWARD:
+	  return round_upward;
+
+	default:
+	  abort ();
+	}
+    }
+  else
+    return round_tonearest;
+}
+
+#endif /* bits/rounding-mode.h */
diff --git a/ports/sysdeps/powerpc/nofpu/bits/rounding-mode.h b/ports/sysdeps/powerpc/nofpu/bits/rounding-mode.h
new file mode 100644
index 0000000..1d0d94f
--- /dev/null
+++ b/ports/sysdeps/powerpc/nofpu/bits/rounding-mode.h
@@ -0,0 +1,51 @@
+/* Determine floating-point rounding mode within libc.  PowerPC
+   soft-float version.
+   Copyright (C) 2012 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef _POWERPC_NOFPU_BITS_ROUNDING_MODE_H
+#define _POWERPC_NOFPU_BITS_ROUNDING_MODE_H	1
+
+#include <fenv.h>
+
+#include "soft-supp.h"
+
+/* Return the floating-point rounding mode.  */
+
+static inline rounding_mode_t
+get_rounding_mode (void)
+{
+  switch (__sim_round_mode)
+    {
+    case FE_DOWNWARD:
+      return round_downward;
+
+    case FE_TONEAREST:
+      return round_tonearest;
+
+    case FE_TOWARDZERO:
+      return round_towardzero;
+
+    case FE_UPWARD:
+      return round_upward;
+
+    default:
+      abort ();
+    }
+}
+
+#endif /* bits/rounding-mode.h */
diff --git a/stdlib/Makefile b/stdlib/Makefile
index dfc5eaf..c730b47 100644
--- a/stdlib/Makefile
+++ b/stdlib/Makefile
@@ -150,3 +150,4 @@ else
 link-libm = $(common-objpfx)math/libm.a
 endif
 $(objpfx)bug-getcontext: $(link-libm)
+$(objpfx)tst-strtod-round: $(link-libm)
diff --git a/stdlib/gen-tst-strtod-round.c b/stdlib/gen-tst-strtod-round.c
index 0a89ff7..18b1759 100644
--- a/stdlib/gen-tst-strtod-round.c
+++ b/stdlib/gen-tst-strtod-round.c
@@ -18,6 +18,7 @@
    <http://www.gnu.org/licenses/>.  */
 
 #define _GNU_SOURCE
+#include <assert.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -65,7 +66,19 @@ round_str (const char *s, const char *suffix,
   mpfr_init (f);
   int r = string_to_fp (f, s, MPFR_RNDD);
   if (need_exact)
-    mpfr_printf ("\t%s,\n", r ? "false" : "true");
+    {
+      assert (prec == 106 && emin == -1073 && emax == 1024);
+      /* The maximum value in IBM long double has discontiguous
+	 mantissa bits.  */
+      mpfr_t max_value;
+      mpfr_init2 (max_value, 107);
+      mpfr_set_str (max_value, "0x1.fffffffffffff7ffffffffffffcp+1023", 0,
+		    MPFR_RNDN);
+      if (mpfr_cmpabs (f, max_value) > 0)
+	r = 1;
+      mpfr_printf ("\t%s,\n", r ? "false" : "true");
+      mpfr_clear (max_value);
+    }
   print_fp (f, suffix, ",\n");
   string_to_fp (f, s, MPFR_RNDN);
   print_fp (f, suffix, ",\n");
diff --git a/stdlib/strtod_l.c b/stdlib/strtod_l.c
index ccd117a..d193441 100644
--- a/stdlib/strtod_l.c
+++ b/stdlib/strtod_l.c
@@ -61,6 +61,7 @@ extern unsigned long long int ____strtoull_l_internal (const char *, char **,
 #include <stdlib.h>
 #include <string.h>
 #include <stdint.h>
+#include <rounding-mode.h>
 
 /* The gmp headers need some configuration frobs.  */
 #define HAVE_ALLOCA 1
@@ -126,6 +127,8 @@ extern unsigned long long int ____strtoull_l_internal (const char *, char **,
 #define	MIN_EXP		PASTE(FLT,_MIN_EXP)
 #define MAX_10_EXP	PASTE(FLT,_MAX_10_EXP)
 #define MIN_10_EXP	PASTE(FLT,_MIN_10_EXP)
+#define MAX_VALUE	PASTE(FLT,_MAX)
+#define MIN_VALUE	PASTE(FLT,_MIN)
 
 /* Extra macros required to get FLT expanded before the pasting.  */
 #define PASTE(a,b)	PASTE1(a,b)
@@ -172,6 +175,34 @@ extern const mp_limb_t _tens_in_limb[MAX_DIG_PER_LIMB + 1];
 	memcpy (dst, src, (dst##size = src##size) * sizeof (mp_limb_t))
 
 
+/* Set errno and return an overflowing value with sign specified by
+   NEGATIVE.  */
+static FLOAT
+overflow_value (int negative)
+{
+  __set_errno (ERANGE);
+#if FLT_EVAL_METHOD != 0
+  volatile
+#endif
+  FLOAT result = (negative ? -MAX_VALUE : MAX_VALUE) * MAX_VALUE;
+  return result;
+}
+
+
+/* Set errno and return an underflowing value with sign specified by
+   NEGATIVE.  */
+static FLOAT
+underflow_value (int negative)
+{
+  __set_errno (ERANGE);
+#if FLT_EVAL_METHOD != 0
+  volatile
+#endif
+  FLOAT result = (negative ? -MIN_VALUE : MIN_VALUE) * MIN_VALUE;
+  return result;
+}
+
+
 /* Return a floating point number of the needed type according to the given
    multi-precision number after possible rounding.  */
 static FLOAT
@@ -181,10 +212,7 @@ round_and_return (mp_limb_t *retval, intmax_t exponent, int negative,
   if (exponent < MIN_EXP - 1)
     {
       if (exponent < MIN_EXP - 1 - MANT_DIG)
-	{
-	  __set_errno (ERANGE);
-	  return negative ? -0.0 : 0.0;
-	}
+	return underflow_value (negative);
 
       mp_size_t shift = MIN_EXP - 1 - exponent;
 
@@ -237,9 +265,14 @@ round_and_return (mp_limb_t *retval, intmax_t exponent, int negative,
   if (exponent > MAX_EXP)
     goto overflow;
 
-  if ((round_limb & (((mp_limb_t) 1) << round_bit)) != 0
-      && (more_bits || (retval[0] & 1) != 0
-	  || (round_limb & ((((mp_limb_t) 1) << round_bit) - 1)) != 0))
+  rounding_mode_t mode = get_rounding_mode ();
+
+  if (round_away (negative,
+		  (retval[0] & 1) != 0,
+		  (round_limb & (((mp_limb_t) 1) << round_bit)) != 0,
+		  (more_bits
+		   || (round_limb & ((((mp_limb_t) 1) << round_bit) - 1)) != 0),
+		  mode))
     {
       mp_limb_t cy = __mpn_add_1 (retval, retval, RETURN_LIMB_SIZE, 1);
 
@@ -263,7 +296,7 @@ round_and_return (mp_limb_t *retval, intmax_t exponent, int negative,
 
   if (exponent > MAX_EXP)
   overflow:
-    return negative ? -FLOAT_HUGE_VAL : FLOAT_HUGE_VAL;
+    return overflow_value (negative);
 
   return MPN2FLOAT (retval, exponent, negative);
 }
@@ -914,9 +947,9 @@ ____STRTOF_INTERNAL (nptr, endptr, group, loc)
 		  else
 		    {
 		      /* Overflow or underflow.  */
-		      __set_errno (ERANGE);
-		      result = (exp_negative ? (negative ? -0.0 : 0.0) :
-				negative ? -FLOAT_HUGE_VAL : FLOAT_HUGE_VAL);
+		      result = (exp_negative
+				? underflow_value (negative)
+				: overflow_value (negative));
 		    }
 
 		  /* Accept all following digits as part of the exponent.  */
@@ -1112,16 +1145,10 @@ ____STRTOF_INTERNAL (nptr, endptr, group, loc)
   }
 
   if (__builtin_expect (exponent > MAX_10_EXP + 1 - (intmax_t) int_no, 0))
-    {
-      __set_errno (ERANGE);
-      return negative ? -FLOAT_HUGE_VAL : FLOAT_HUGE_VAL;
-    }
+    return overflow_value (negative);
 
   if (__builtin_expect (exponent < MIN_10_EXP - (DIG + 1), 0))
-    {
-      __set_errno (ERANGE);
-      return negative ? -0.0 : 0.0;
-    }
+    return underflow_value (negative);
 
   if (int_no > 0)
     {
@@ -1182,10 +1209,7 @@ ____STRTOF_INTERNAL (nptr, endptr, group, loc)
       /* Now we know the exponent of the number in base two.
 	 Check it against the maximum possible exponent.  */
       if (__builtin_expect (bits > MAX_EXP, 0))
-	{
-	  __set_errno (ERANGE);
-	  return negative ? -FLOAT_HUGE_VAL : FLOAT_HUGE_VAL;
-	}
+	return overflow_value (negative);
 
       /* We have already the first BITS bits of the result.  Together with
 	 the information whether more non-zero bits follow this is enough
diff --git a/stdlib/tst-strtod-round.c b/stdlib/tst-strtod-round.c
index 07881e3..e357158 100644
--- a/stdlib/tst-strtod-round.c
+++ b/stdlib/tst-strtod-round.c
@@ -17,6 +17,7 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
+#include <fenv.h>
 #include <float.h>
 #include <math.h>
 #include <stdbool.h>
@@ -24,38 +25,70 @@
 #include <stdlib.h>
 #include <string.h>
 
-struct test {
-  const char *s;
+struct test_results {
   float f;
   double d;
-  bool ld_ok;
   long double ld;
 };
 
+struct test {
+  const char *s;
+  bool ld_ok;
+  struct test_results rd, rn, rz, ru;
+};
+
 #if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
 # define TEST(s, fd, fn, fz, fu, dd, dn, dz, du, ld53d, ld53n, ld53z, ld53u, \
 	      ld64d, ld64n, ld64z, ld64u, ld106exact,			\
 	      ld106d, ld106n, ld106z, ld106u,				\
 	      ld113d, ld113n, ld113z, ld113u)				\
-  { s, fn, dn, true, ld53n }
+  {									\
+    s,									\
+    true,								\
+    { fd, dd, ld53d },							\
+    { fn, dn, ld53n },							\
+    { fz, dz, ld53z },							\
+    { fu, du, ld53u }							\
+  }
 #elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384
 # define TEST(s, fd, fn, fz, fu, dd, dn, dz, du, ld53d, ld53n, ld53z, ld53u, \
 	      ld64d, ld64n, ld64z, ld64u, ld106exact,			\
 	      ld106d, ld106n, ld106z, ld106u,				\
 	      ld113d, ld113n, ld113z, ld113u)				\
-  { s, fn, dn, true, ld64n }
+  {									\
+    s,									\
+    true,								\
+    { fd, dd, ld64d },							\
+    { fn, dn, ld64n },							\
+    { fz, dz, ld64z },							\
+    { fu, du, ld64u }							\
+  }
 #elif LDBL_MANT_DIG == 106 && LDBL_MAX_EXP == 1024
 # define TEST(s, fd, fn, fz, fu, dd, dn, dz, du, ld53d, ld53n, ld53z, ld53u, \
 	      ld64d, ld64n, ld64z, ld64u, ld106exact,			\
 	      ld106d, ld106n, ld106z, ld106u,				\
 	      ld113d, ld113n, ld113z, ld113u)				\
-  { s, fn, dn, ld106exact, ld106n }
+  {									\
+    s,									\
+    ld106exact,								\
+    { fd, dd, ld106d },							\
+    { fn, dn, ld106n },							\
+    { fz, dz, ld106z },							\
+    { fu, du, ld106u }							\
+  }
 #elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
 # define TEST(s, fd, fn, fz, fu, dd, dn, dz, du, ld53d, ld53n, ld53z, ld53u, \
 	      ld64d, ld64n, ld64z, ld64u, ld106exact,			\
 	      ld106d, ld106n, ld106z, ld106u,				\
 	      ld113d, ld113n, ld113z, ld113u)				\
-  { s, fn, dn, true, ld113n }
+  {									\
+    s,									\
+    true,								\
+    { fd, dd, ld113d },							\
+    { fn, dn, ld113n },							\
+    { fz, dz, ld113z },							\
+    { fu, du, ld113u }							\
+  }
 #else
 # error "unknown long double format"
 #endif
@@ -1670,7 +1703,7 @@ static const struct test tests[] = {
 	0xf.ffffffffffffcp+1020L,
 	0xf.ffffffffffffcp+1020L,
 	0xf.ffffffffffffcp+1020L,
-	true,
+	false,
 	0xf.ffffffffffffcp+1020L,
 	0xf.ffffffffffffcp+1020L,
 	0xf.ffffffffffffcp+1020L,
@@ -1763,7 +1796,7 @@ static const struct test tests[] = {
 	-0xf.ffffffffffffcp+1020L,
 	-0xf.ffffffffffffcp+1020L,
 	-0xf.ffffffffffffcp+1020L,
-	true,
+	false,
 	-0xf.ffffffffffffcp+1020L,
 	-0xf.ffffffffffffcp+1020L,
 	-0xf.ffffffffffffcp+1020L,
@@ -6362,38 +6395,73 @@ static const struct test tests[] = {
 };
 
 static int
+test_in_one_mode (const char *s, const struct test_results *expected,
+		  bool ld_ok, const char *mode_name)
+{
+  int result = 0;
+  float f = strtof (s, NULL);
+  double d = strtod (s, NULL);
+  long double ld = strtold (s, NULL);
+  if (f != expected->f
+      || copysignf (1.0f, f) != copysignf (1.0f, expected->f))
+    {
+      printf ("strtof (%s) returned %a not %a (%s)\n", s, f,
+	      expected->f, mode_name);
+      result = 1;
+    }
+  if (d != expected->d
+      || copysign (1.0, d) != copysign (1.0, expected->d))
+    {
+      printf ("strtod (%s) returned %a not %a (%s)\n", s, d,
+	      expected->d, mode_name);
+      result = 1;
+    }
+  if (ld != expected->ld
+      || copysignl (1.0L, ld) != copysignl (1.0L, expected->ld))
+    {
+      printf ("strtold (%s) returned %La not %La (%s)\n", s, ld,
+	      expected->ld, mode_name);
+      if (ld_ok)
+	result = 1;
+      else
+	printf ("ignoring this inexact long double result\n");
+    }
+  return result;
+}
+
+static int
 do_test (void)
 {
+  int save_round_mode = fegetround ();
   int result = 0;
   for (size_t i = 0; i < sizeof (tests) / sizeof (tests[0]); i++)
     {
-      float f = strtof (tests[i].s, NULL);
-      double d = strtod (tests[i].s, NULL);
-      long double ld = strtold (tests[i].s, NULL);
-      if (f != tests[i].f
-	  || copysignf (1.0f, f) != copysignf (1.0f, tests[i].f))
+      result |= test_in_one_mode (tests[i].s, &tests[i].rn, tests[i].ld_ok,
+				  "default rounding mode");
+#ifdef FE_DOWNWARD
+      if (!fesetround (FE_DOWNWARD))
 	{
-	  printf ("strtof (%s) returned %a not %a\n", tests[i].s, f,
-		  tests[i].f);
-	  result = 1;
+	  result |= test_in_one_mode (tests[i].s, &tests[i].rd, tests[i].ld_ok,
+				      "FE_DOWNWARD");
+	  fesetround (save_round_mode);
 	}
-      if (d != tests[i].d
-	  || copysign (1.0, d) != copysign (1.0, tests[i].d))
+#endif
+#ifdef FE_TOWARDZERO
+      if (!fesetround (FE_TOWARDZERO))
 	{
-	  printf ("strtod (%s) returned %a not %a\n", tests[i].s, d,
-		  tests[i].d);
-	  result = 1;
+	  result |= test_in_one_mode (tests[i].s, &tests[i].rz, tests[i].ld_ok,
+				      "FE_TOWARDZERO");
+	  fesetround (save_round_mode);
 	}
-      if (ld != tests[i].ld
-	  || copysignl (1.0L, ld) != copysignl (1.0L, tests[i].ld))
+#endif
+#ifdef FE_UPWARD
+      if (!fesetround (FE_UPWARD))
 	{
-	  printf ("strtold (%s) returned %La not %La\n", tests[i].s, ld,
-		  tests[i].ld);
-	  if (tests[i].ld_ok)
-	    result = 1;
-	  else
-	    printf ("ignoring this inexact long double result\n");
+	  result |= test_in_one_mode (tests[i].s, &tests[i].ru, tests[i].ld_ok,
+				      "FE_UPWARD");
+	  fesetround (save_round_mode);
 	}
+#endif
     }
   return result;
 }
diff --git a/sysdeps/s390/fpu/bits/rounding-mode.h b/sysdeps/s390/fpu/bits/rounding-mode.h
new file mode 100644
index 0000000..3376597
--- /dev/null
+++ b/sysdeps/s390/fpu/bits/rounding-mode.h
@@ -0,0 +1,53 @@
+/* Determine floating-point rounding mode within libc.  S/390 version.
+   Copyright (C) 2012 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef _BITS_ROUNDING_MODE_H
+#define _BITS_ROUNDING_MODE_H	1
+
+#include <fenv.h>
+#include <fenv_libc.h>
+#include <fpu_control.h>
+
+/* Return the floating-point rounding mode.  */
+
+static inline rounding_mode_t
+get_rounding_mode (void)
+{
+  fpu_control_t fc;
+
+  _FPU_GETCW (fc);
+  switch (fc & FPC_RM_MASK)
+    {
+    case FE_DOWNWARD:
+      return round_downward;
+
+    case FE_TONEAREST:
+      return round_tonearest;
+
+    case FE_TOWARDZERO:
+      return round_towardzero;
+
+    case FE_UPWARD:
+      return round_upward;
+
+    default:
+      abort ();
+    }
+}
+
+#endif /* bits/rounding-mode.h */

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: Make strtod respect the rounding mode (bug 14518)
  2012-08-29 23:46 Make strtod respect the rounding mode (bug 14518) Joseph S. Myers
@ 2012-08-30 19:56 ` Richard Henderson
  2012-08-30 20:05   ` Joseph S. Myers
  0 siblings, 1 reply; 3+ messages in thread
From: Richard Henderson @ 2012-08-30 19:56 UTC (permalink / raw)
  To: Joseph S. Myers
  Cc: libc-alpha, libc-ports, Andreas Krebbel, Ryan S. Arnold,
	Mike Frysinger, Carlos O'Donell

[-- Attachment #1: Type: text/plain, Size: 1009 bytes --]

On 08/29/2012 04:45 PM, Joseph S. Myers wrote:
> * This patch does not update the alpha port.  alpha maintainers may
>   wish to add a definition of _FPU_GETCW to fpu_control.h, or to add
>   bits/rounding-mode.h for alpha (I don't know why its fpu_control.h
>   doesn't define _FPU_GETCW when it has the other macros needed).

I'm not sure why we implement as much of fpu_control.h as we do.
It's somewhat unfortunate that it exists in its current form at
all, because it's all very biased toward binary compatibility with
the x86 fpcr format.

While I *could* commit a patch like the following, I'd like an
opinion on whether the existing defines could be trashed and the
file re-imagined as something more friendly for native.  Given
of course that at present there cannot be any users since the
actual _FPU_G/SETCW macros do not exist.

Or whether the file should be trashed and I'll just implement
bits/rounding-mode.h.  Which honestly seems like it'd be the
most efficient for the use in question.


r~

[-- Attachment #2: 0001-alpha-Implement-_FPU_GETCW-and-_FPU_SETCW.patch --]
[-- Type: text/x-patch, Size: 6833 bytes --]

From 6a480a617d97dec79140ff449197773c45714024 Mon Sep 17 00:00:00 2001
From: Richard Henderson <rth@twiddle.net>
Date: Thu, 30 Aug 2012 12:06:05 -0700
Subject: [PATCH] alpha: Implement _FPU_GETCW and _FPU_SETCW

---
 ports/ChangeLog.alpha                 |  6 +++
 ports/sysdeps/alpha/Makefile          |  1 +
 ports/sysdeps/alpha/fpu/Versions      |  4 ++
 ports/sysdeps/alpha/fpu/fpu_control.h | 15 ++++++-
 ports/sysdeps/alpha/fpu/fpucw.c       | 77 +++++++++++++++++++++++++++++++++++
 5 files changed, 101 insertions(+), 2 deletions(-)
 create mode 100644 ports/sysdeps/alpha/fpu/fpucw.c

diff --git a/ports/ChangeLog.alpha b/ports/ChangeLog.alpha
index 9589dd3..12bc4fb 100644
--- a/ports/ChangeLog.alpha
+++ b/ports/ChangeLog.alpha
@@ -1,5 +1,11 @@
 2012-08-30  Richard Henderson  <rth@redhat.com>
 
+	* sysdeps/alpha/fpu/fpucw.c: New file.
+	* sysdeps/alpha/Makefile [math] (sysdep_routines): Add it.
+	* sysdeps/alpha/fpu/fpu_control.h (__fpu_getcw): Declare.
+	(__fpu_setcw, _FPU_GETCW, _FPU_SETCW): Declare
+	* sysdeps/alpha/fpu/Versions (__fpu_getcw, __fpu_setcw): Add for 2.17.
+
 	* sysdeps/alpha/fpu/s_isnan.c: Define all aliases in terms of
 	the original __isnan symbol.
 
diff --git a/ports/sysdeps/alpha/Makefile b/ports/sysdeps/alpha/Makefile
index 1a80db8..75ad30e 100644
--- a/ports/sysdeps/alpha/Makefile
+++ b/ports/sysdeps/alpha/Makefile
@@ -43,6 +43,7 @@ CFLAGS-s_fma.c = -mieee-with-inexact
 CFLAGS-s_fmaf.c = -mieee-with-inexact
 # This test tries to check for inexact being raised by arithmetic.
 CFLAGS-test-misc.c += -mieee-with-inexact
+sysdep_routines += fpucw
 endif
 
 # Build everything with full IEEE math support, and with dynamic rounding;
diff --git a/ports/sysdeps/alpha/fpu/Versions b/ports/sysdeps/alpha/fpu/Versions
index c9b0e03..ad4eae6 100644
--- a/ports/sysdeps/alpha/fpu/Versions
+++ b/ports/sysdeps/alpha/fpu/Versions
@@ -3,6 +3,10 @@ libc {
     # functions used in other libraries
     __ieee_get_fp_control; __ieee_set_fp_control;
   }
+  GLIBC_2.17 {
+    # functions implementing fpu_control.h
+    __fpu_getcw; __fpu_setcw;
+  }
 }
 libm {
   GLIBC_2.3.4 {
diff --git a/ports/sysdeps/alpha/fpu/fpu_control.h b/ports/sysdeps/alpha/fpu/fpu_control.h
index 4027300..68f06ef 100644
--- a/ports/sysdeps/alpha/fpu/fpu_control.h
+++ b/ports/sysdeps/alpha/fpu/fpu_control.h
@@ -1,5 +1,5 @@
 /* FPU control word bits.  Alpha-mapped-to-Intel version.
-   Copyright (C) 1996, 1998, 2000, 2002 Free Software Foundation, Inc.
+   Copyright (C) 1996-2012 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Olaf Flebbe.
 
@@ -80,7 +80,6 @@
 
 #define _FPU_RESERVED 0xF0C0  /* Reserved bits in cw */
 
-
 /* Now two recommended cw */
 
 /* Linux default:
@@ -102,4 +101,16 @@ typedef unsigned int fpu_control_t;
 /* Default control word set at startup.  */
 extern fpu_control_t __fpu_control;
 
+/* Internal functions for accessing the hardware control word.  */
+extern fpu_control_t __fpu_getcw(void);
+extern void __fpu_setcw(fpu_control_t);
+
+/* The "standard" macros for accessing the hardware control word.
+   The x86 port which we are emulating uses __asm__ in these macros,
+   which implies the macro is syntactically a statement and not an
+   expression.  Mirror this restriction here by wrapping the calls
+   within a do/while statement.  */
+#define _FPU_GETCW(cw) do { (cw) = __fpu_getcw(); } while (0)
+#define _FPU_SETCW(cw) do { __fpu_setcw(cw); } while (0)
+
 #endif	/* _ALPHA_FPU_CONTROL */
diff --git a/ports/sysdeps/alpha/fpu/fpucw.c b/ports/sysdeps/alpha/fpu/fpucw.c
new file mode 100644
index 0000000..bcab24a
--- /dev/null
+++ b/ports/sysdeps/alpha/fpu/fpucw.c
@@ -0,0 +1,77 @@
+/* Manipulate the fpu environment in an x86 compatible way.
+   Copyright (C) 2012 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library.  If not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <fpu_control.h>
+#include <fenv_libc.h>
+
+
+#define REPLACE(S, F, T)			\
+  ((F) > (T)					\
+   ? ((S) >> __builtin_ctzl((F) / (T))) & (T)	\
+   : ((S) & (F)) << __builtin_ctzl((T) / (F)))
+
+fpu_control_t
+__fpu_getcw(void)
+{
+  unsigned long int fpcr, swcr, retl;
+
+  /* Get status from software and hardware.  Note that we don't need an
+     excb because the callsys is an implied trap barrier.  */
+  swcr = __ieee_get_fp_control ();
+  __asm__ __volatile__ ("mf_fpcr %0" : "=f" (fpcr));
+
+  /* Extract the rounding mode.  */
+  retl = ((fpcr >> FPCR_ROUND_SHIFT) & 3) << 10;
+
+  /* Extract the Alpha "enabled" exceptions to x86 "masked" exceptions.  */
+  retl |= REPLACE(~swcr, FE_INVALID >> SWCR_ENABLE_SHIFT,   _FPU_MASK_IM);
+  retl |= REPLACE(~swcr, FE_DENORMAL >> SWCR_ENABLE_SHIFT,  _FPU_MASK_DM);
+  retl |= REPLACE(~swcr, FE_DIVBYZERO >> SWCR_ENABLE_SHIFT, _FPU_MASK_ZM);
+  retl |= REPLACE(~swcr, FE_OVERFLOW >> SWCR_ENABLE_SHIFT,  _FPU_MASK_OM);
+  retl |= REPLACE(~swcr, FE_UNDERFLOW >> SWCR_ENABLE_SHIFT, _FPU_MASK_UM);
+  retl |= REPLACE(~swcr, FE_INEXACT >> SWCR_ENABLE_SHIFT,   _FPU_MASK_PM);
+
+  return retl;
+}
+
+void
+__fpu_setcw(fpu_control_t cw)
+{
+  unsigned long int fpcr, swcr, cwl = cw;
+
+  /* Get status from software and hardware.  Note that we don't need an
+     excb because the callsys is an implied trap barrier.  */
+  swcr = __ieee_get_fp_control ();
+  __asm__ __volatile__ ("mf_fpcr %0" : "=f" (fpcr));
+
+  fpcr &= ~FPCR_ROUND_MASK;
+  fpcr |= ((cwl >> 10) & 3ul) << FPCR_ROUND_SHIFT;
+
+  swcr &= ~SWCR_ENABLE_MASK;
+  swcr |= REPLACE(~cwl, _FPU_MASK_IM, FE_INVALID >> SWCR_ENABLE_SHIFT);
+  swcr |= REPLACE(~cwl, _FPU_MASK_DM, FE_DENORMAL >> SWCR_ENABLE_SHIFT);
+  swcr |= REPLACE(~cwl, _FPU_MASK_ZM, FE_DIVBYZERO >> SWCR_ENABLE_SHIFT);
+  swcr |= REPLACE(~cwl, _FPU_MASK_OM, FE_OVERFLOW >> SWCR_ENABLE_SHIFT);
+  swcr |= REPLACE(~cwl, _FPU_MASK_UM, FE_UNDERFLOW >> SWCR_ENABLE_SHIFT);
+  swcr |= REPLACE(~cwl, _FPU_MASK_PM, FE_INEXACT >> SWCR_ENABLE_SHIFT);
+
+  /* Install the new rounding mode and exception enable mask.  Note that
+     the system call is the implied trap barrier for our modification.  */
+  __asm__ __volatile__ ("mt_fpcr %0" : : "f" (fpcr));
+  __ieee_set_fp_control (swcr);
+}
-- 
1.7.11.4


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: Make strtod respect the rounding mode (bug 14518)
  2012-08-30 19:56 ` Richard Henderson
@ 2012-08-30 20:05   ` Joseph S. Myers
  0 siblings, 0 replies; 3+ messages in thread
From: Joseph S. Myers @ 2012-08-30 20:05 UTC (permalink / raw)
  To: Richard Henderson
  Cc: libc-alpha, libc-ports, Andreas Krebbel, Ryan S. Arnold,
	Mike Frysinger, Carlos O'Donell

On Thu, 30 Aug 2012, Richard Henderson wrote:

> While I *could* commit a patch like the following, I'd like an
> opinion on whether the existing defines could be trashed and the
> file re-imagined as something more friendly for native.  Given
> of course that at present there cannot be any users since the
> actual _FPU_G/SETCW macros do not exist.
> 
> Or whether the file should be trashed and I'll just implement
> bits/rounding-mode.h.  Which honestly seems like it'd be the
> most efficient for the use in question.

I have no views on what's right for the alpha port here.

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2012-08-30 20:05 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-08-29 23:46 Make strtod respect the rounding mode (bug 14518) Joseph S. Myers
2012-08-30 19:56 ` Richard Henderson
2012-08-30 20:05   ` Joseph S. Myers

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).