This is the mail archive of the
libc-alpha@sourceware.org
mailing list for the glibc project.
Re: Ping Re: Make strtod respect the rounding mode (bug 14518)
Here is this patch further updated now that the fixes for IBM long
double have done in separately. Tested x86_64 and x86.
2012-09-06 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/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).
ports/ChangeLog.arm:
2012-09-06 Joseph Myers <joseph@codesourcery.com>
* sysdeps/arm/bits/rounding-mode.h: New file.
ports/ChangeLog.powerpc:
2012-09-06 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/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 c6ad126..76385a9 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,21 +25,32 @@
#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
@@ -6819,38 +6859,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