This is the mail archive of the libc-ports@sources.redhat.com mailing list for the libc-ports 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]

Make strtod respect the rounding mode (bug 14518)


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


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