]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
Make strtod respect the rounding mode (bug 14518).
authorJoseph Myers <joseph@codesourcery.com>
Wed, 12 Sep 2012 23:36:19 +0000 (23:36 +0000)
committerJoseph Myers <joseph@codesourcery.com>
Wed, 12 Sep 2012 23:36:19 +0000 (23:36 +0000)
12 files changed:
ChangeLog
NEWS
include/rounding-mode.h [new file with mode: 0644]
ports/ChangeLog.arm
ports/ChangeLog.powerpc
ports/sysdeps/arm/get-rounding-mode.h [new file with mode: 0644]
ports/sysdeps/powerpc/nofpu/get-rounding-mode.h [new file with mode: 0644]
stdlib/Makefile
stdlib/strtod_l.c
stdlib/tst-strtod-round.c
sysdeps/generic/get-rounding-mode.h [new file with mode: 0644]
sysdeps/s390/fpu/get-rounding-mode.h [new file with mode: 0644]

index 5c756e95888874d5bc39ffe9c90a8233f4378937..11120ac8f59b0186f1993cfb9f27e15cdb8a3937 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,30 @@
+2012-09-12  Joseph Myers  <joseph@codesourcery.com>
+
+       [BZ #14518]
+       * include/rounding-mode.h: New file.
+       * sysdeps/generic/get-rounding-mode.h: Likewise.
+       * sysdeps/s390/fpu/get-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/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.
+       (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).
+
 2012-12-09  Allan McRae  <allan@archlinux.org>
 
         * sysdeps/i386/fpu/libm-test-ulps: Update
diff --git a/NEWS b/NEWS
index c67a651621231d3cc241d68ccf5313c49abe15d5..d5d386a12910d9c43a386f1e57507bf1179473d8 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -13,7 +13,7 @@ Version 2.17
   13542, 13717, 13696, 13939, 13966, 14042, 14090, 14166, 14150, 14151,
   14154, 14157, 14166, 14173, 14195, 14237, 14252, 14283, 14298, 14303,
   14307, 14328, 14331, 14336, 14337, 14347, 14349, 14459, 14476, 14505,
-  14510, 14516, 14519, 14532, 14538, 14544, 14545.
+  14510, 14516, 14518, 14519, 14532, 14538, 14544, 14545.
 
 * Support for STT_GNU_IFUNC symbols added for s390 and s390x.
   Optimized versions of memcpy, memset, and memcmp added for System z10 and
diff --git a/include/rounding-mode.h b/include/rounding-mode.h
new file mode 100644 (file)
index 0000000..bde41e7
--- /dev/null
@@ -0,0 +1,65 @@
+/* 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 <fenv.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+/* Get the architecture-specific definition of how to determine the
+   rounding mode in libc.  This header must also define the FE_*
+   macros for any standard rounding modes the architecture does not
+   have in <fenv.h>, to arbitrary distinct values.  */
+#include <get-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,
+           int mode)
+{
+  switch (mode)
+    {
+    case FE_DOWNWARD:
+      return negative && (half_bit || more_bits);
+
+    case FE_TONEAREST:
+      return half_bit && (last_digit_odd || more_bits);
+
+    case FE_TOWARDZERO:
+      return false;
+
+    case FE_UPWARD:
+      return !negative && (half_bit || more_bits);
+
+    default:
+      abort ();
+    }
+}
+
+#endif /* rounding-mode.h */
index 1fae5f72f865b2a9e1ab0c1dab624e3600f99866..a0c583b9973c54b438d1c80beac643cd1d90f606 100644 (file)
@@ -1,3 +1,7 @@
+2012-09-12  Joseph Myers  <joseph@codesourcery.com>
+
+       * sysdeps/arm/get-rounding-mode.h: New file.
+
 2012-08-27  Joseph Myers  <joseph@codesourcery.com>
 
        * sysdeps/unix/sysv/linux/arm/kernel-features.h
index c8a85840009aefa417726828cb5a33c5d76ca4b9..af98f828ea4453f790664cda668d0f494ffd94f3 100644 (file)
@@ -1,3 +1,7 @@
+2012-09-12  Joseph Myers  <joseph@codesourcery.com>
+
+       * sysdeps/powerpc/nofpu/get-rounding-mode.h: New file.
+
 2012-07-25  Florian Weimer  <fweimer@redhat.com>
 
        * sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/nptl/libc.abilist:
diff --git a/ports/sysdeps/arm/get-rounding-mode.h b/ports/sysdeps/arm/get-rounding-mode.h
new file mode 100644 (file)
index 0000000..a614fe4
--- /dev/null
@@ -0,0 +1,42 @@
+/* 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_GET_ROUNDING_MODE_H
+#define _ARM_GET_ROUNDING_MODE_H       1
+
+#include <arm-features.h>
+#include <fenv.h>
+#include <fpu_control.h>
+
+/* Return the floating-point rounding mode.  */
+
+static inline int
+get_rounding_mode (void)
+{
+  if (ARM_HAVE_VFP)
+    {
+      fpu_control_t fc;
+
+      _FPU_GETCW (fc);
+      return fc & FE_TOWARDZERO;
+    }
+  else
+    return FE_TONEAREST;
+}
+
+#endif /* get-rounding-mode.h */
diff --git a/ports/sysdeps/powerpc/nofpu/get-rounding-mode.h b/ports/sysdeps/powerpc/nofpu/get-rounding-mode.h
new file mode 100644 (file)
index 0000000..14be20f
--- /dev/null
@@ -0,0 +1,35 @@
+/* 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_GET_ROUNDING_MODE_H
+#define _POWERPC_NOFPU_GET_ROUNDING_MODE_H     1
+
+#include <fenv.h>
+
+#include "soft-supp.h"
+
+/* Return the floating-point rounding mode.  */
+
+static inline int
+get_rounding_mode (void)
+{
+  return __sim_round_mode;
+}
+
+#endif /* get-rounding-mode.h */
index dfc5eaf97bf0c0f2f988f83779adb21184e65769..c730b4743394e699978141987ca6109b6351bf38 100644 (file)
@@ -150,3 +150,4 @@ else
 link-libm = $(common-objpfx)math/libm.a
 endif
 $(objpfx)bug-getcontext: $(link-libm)
+$(objpfx)tst-strtod-round: $(link-libm)
index ccd117a9f1021bfdf576a8ef7da94cc2dca2bf16..95f13e40a2b26cde1821ebb59f6f16ca5dba3ff5 100644 (file)
@@ -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))
+  int 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
index c6ad1268733072438578f0322bf60a6d3be371f6..76385a94d347e3e4e7e97ef534bc6ac6d04833f6 100644 (file)
@@ -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>
 #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,  \
              ld64id, ld64in, ld64iz, ld64iu,                                 \
              ld64md, ld64mn, ld64mz, ld64mu,                                 \
              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 && LDBL_MIN_EXP == -16381
 /* This is for the Intel extended float format.  */
 # define TEST(s, fd, fn, fz, fu, dd, dn, dz, du, ld53d, ld53n, ld53z, ld53u,  \
@@ -46,7 +58,14 @@ struct test {
              ld64md, ld64mn, ld64mz, ld64mu,                                 \
              ld106exact, ld106d, ld106n, ld106z, ld106u,                     \
              ld113d, ld113n, ld113z, ld113u)                                 \
-  { s, fn, dn, true, ld64in }
+  {                                                                    \
+    s,                                                                 \
+    true,                                                              \
+    { fd, dd, ld64id },                                                        \
+    { fn, dn, ld64in },                                                        \
+    { fz, dz, ld64iz },                                                        \
+    { fu, du, ld64iu }                                                 \
+  }
 #elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384 && LDBL_MIN_EXP == -16382
 /* This is for the Motorola extended float format.  */
 # define TEST(s, fd, fn, fz, fu, dd, dn, dz, du, ld53d, ld53n, ld53z, ld53u,  \
@@ -54,21 +73,42 @@ struct test {
              ld64md, ld64mn, ld64mz, ld64mu,                                 \
              ld106exact, ld106d, ld106n, ld106z, ld106u,                     \
              ld113d, ld113n, ld113z, ld113u)                                 \
-  { s, fn, dn, true, ld64mn }
+  {                                                                    \
+    s,                                                                 \
+    true,                                                              \
+    { fd, dd, ld64md },                                                        \
+    { fn, dn, ld64mn },                                                        \
+    { fz, dz, ld64mz },                                                        \
+    { fu, du, ld64mu }                                                 \
+  }
 #elif LDBL_MANT_DIG == 106 && LDBL_MAX_EXP == 1024
 # define TEST(s, fd, fn, fz, fu, dd, dn, dz, du, ld53d, ld53n, ld53z, ld53u,  \
              ld64id, ld64in, ld64iz, ld64iu,                                 \
              ld64md, ld64mn, ld64mz, ld64mu,                                 \
              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,  \
              ld64id, ld64in, ld64iz, ld64iu,                                 \
              ld64md, ld64mn, ld64mz, ld64mu,                                 \
              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
@@ -6818,39 +6858,74 @@ static const struct test tests[] = {
        -0x0p+0L),
 };
 
+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/generic/get-rounding-mode.h b/sysdeps/generic/get-rounding-mode.h
new file mode 100644 (file)
index 0000000..0ecaddd
--- /dev/null
@@ -0,0 +1,124 @@
+/* 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 _GET_ROUNDING_MODE_H
+#define _GET_ROUNDING_MODE_H   1
+
+#include <fpu_control.h>
+
+/* Define values for FE_* modes not defined for this architecture.  */
+#ifdef FE_DOWNWARD
+# define ORIG_FE_DOWNWARD FE_DOWNWARD
+#else
+# define ORIG_FE_DOWNWARD 0
+#endif
+#ifdef FE_TONEAREST
+# define ORIG_FE_TONEAREST FE_TONEAREST
+#else
+# define ORIG_FE_TONEAREST 0
+#endif
+#ifdef FE_TOWARDZERO
+# define ORIG_FE_TOWARDZERO FE_TOWARDZERO
+#else
+# define ORIG_FE_TOWARDZERO 0
+#endif
+#ifdef FE_UPWARD
+# define ORIG_FE_UPWARD FE_UPWARD
+#else
+# define ORIG_FE_UPWARD 0
+#endif
+#define FE_CONSTRUCT_DISTINCT_VALUE(X, Y, Z) \
+  ((((X) & 1) | ((Y) & 2) | ((Z) & 4)) ^ 7)
+#ifndef FE_DOWNWARD
+# define FE_DOWNWARD FE_CONSTRUCT_DISTINCT_VALUE (ORIG_FE_TONEAREST,   \
+                                                 ORIG_FE_TOWARDZERO,   \
+                                                 ORIG_FE_UPWARD)
+#endif
+#ifndef FE_TONEAREST
+# define FE_TONEAREST FE_CONSTRUCT_DISTINCT_VALUE (FE_DOWNWARD,                \
+                                                  ORIG_FE_TOWARDZERO,  \
+                                                  ORIG_FE_UPWARD)
+#endif
+#ifndef FE_TOWARDZERO
+# define FE_TOWARDZERO FE_CONSTRUCT_DISTINCT_VALUE (FE_DOWNWARD,       \
+                                                   FE_TONEAREST,       \
+                                                   ORIG_FE_UPWARD)
+#endif
+#ifndef FE_UPWARD
+# define FE_UPWARD FE_CONSTRUCT_DISTINCT_VALUE (FE_DOWNWARD,   \
+                                               FE_TONEAREST,   \
+                                               FE_TOWARDZERO)
+#endif
+
+/* Return the floating-point rounding mode.  */
+
+static inline int
+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 FE_DOWNWARD;
+# endif
+
+# ifdef _FPU_RC_NEAREST
+    case _FPU_RC_NEAREST:
+      return FE_TONEAREST;
+# endif
+
+# ifdef _FPU_RC_ZERO
+    case _FPU_RC_ZERO:
+      return FE_TOWARDZERO;
+# endif
+
+# ifdef _FPU_RC_UP
+    case _FPU_RC_UP:
+      return FE_UPWARD;
+# endif
+
+    default:
+      abort ();
+    }
+#else
+  return FE_TONEAREST;
+#endif
+}
+
+#endif /* get-rounding-mode.h */
diff --git a/sysdeps/s390/fpu/get-rounding-mode.h b/sysdeps/s390/fpu/get-rounding-mode.h
new file mode 100644 (file)
index 0000000..3e427ec
--- /dev/null
@@ -0,0 +1,37 @@
+/* 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 _S390_GET_ROUNDING_MODE_H
+#define _S390_GET_ROUNDING_MODE_H      1
+
+#include <fenv.h>
+#include <fenv_libc.h>
+#include <fpu_control.h>
+
+/* Return the floating-point rounding mode.  */
+
+static inline int
+get_rounding_mode (void)
+{
+  fpu_control_t fc;
+
+  _FPU_GETCW (fc);
+  return fc & FPC_RM_MASK;
+}
+
+#endif /* get-rounding-mode.h */