* lib/fenv.in.h (fegetround, fesetround): New declarations.
* lib/fenv-private.h: New file, based on glibc.
* lib/fenv-round.c: New file, based on glibc.
* m4/mathfunc.m4 (gl_MATHFUNC): Handle also the type fp_rnd.
* m4/fenv-rounding.m4: New file.
* modules/fenv (Depends-on): Add snippet/c++defs.
(Makefile.am): Substitute $(CXXDEFS_H) into fenv.h.
* modules/fenv-rounding: New file.
* doc/posix-functions/fegetround.texi: Mention the new module.
* doc/posix-functions/fesetround.texi: Likewise.
+2023-10-27 Bruno Haible <bruno@clisp.org>
+
+ fenv-rounding: New module.
+ * lib/fenv.in.h (fegetround, fesetround): New declarations.
+ * lib/fenv-private.h: New file, based on glibc.
+ * lib/fenv-round.c: New file, based on glibc.
+ * m4/mathfunc.m4 (gl_MATHFUNC): Handle also the type fp_rnd.
+ * m4/fenv-rounding.m4: New file.
+ * modules/fenv (Depends-on): Add snippet/c++defs.
+ (Makefile.am): Substitute $(CXXDEFS_H) into fenv.h.
+ * modules/fenv-rounding: New file.
+ * doc/posix-functions/fegetround.texi: Mention the new module.
+ * doc/posix-functions/fesetround.texi: Likewise.
+
2023-10-27 Bruno Haible <bruno@clisp.org>
tests: Use C99 initializer syntax for memory_long_double.
POSIX specification:@* @url{https://pubs.opengroup.org/onlinepubs/9699919799/functions/fegetround.html}
-Gnulib module: ---
+Gnulib module: fenv-rounding
Portability problems fixed by Gnulib:
@itemize
+@item
+This function is missing on some platforms:
+FreeBSD 6.0, NetBSD 5.0, OpenBSD 3.8, AIX 5.1, IRIX 6.5, Solaris 9, Cygwin 1.7.7, MSVC 9, Android 4.4.
@end itemize
Portability problems not fixed by Gnulib:
@itemize
-@item
-This function is missing on some platforms:
-FreeBSD 6.0, NetBSD 5.0, OpenBSD 3.8, AIX 5.1, IRIX 6.5, Solaris 9, Cygwin 1.7.7, MSVC 9, Android 4.4.
@end itemize
POSIX specification:@* @url{https://pubs.opengroup.org/onlinepubs/9699919799/functions/fesetround.html}
-Gnulib module: ---
+Gnulib module: fenv-rounding
Portability problems fixed by Gnulib:
@itemize
+@item
+This function is missing on some platforms:
+FreeBSD 6.0, NetBSD 5.0, OpenBSD 3.8, AIX 5.1, IRIX 6.5, Solaris 9, Cygwin 1.7.7, MSVC 9, Android 4.4.
+@item
+This function does not work on some platforms:
+MSVC 14.
@end itemize
Portability problems not fixed by Gnulib:
@itemize
-@item
-This function is missing on some platforms:
-FreeBSD 6.0, NetBSD 5.0, OpenBSD 3.8, AIX 5.1, IRIX 6.5, Solaris 9, Cygwin 1.7.7, MSVC 9, Android 4.4.
@end itemize
--- /dev/null
+/* Common definitions for the implementation of the various <fenv.h> modules.
+ Copyright (C) 1997-2023 Free Software Foundation, Inc.
+
+ This file 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.
+
+ This file 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 this program. If not, see <https://www.gnu.org/licenses/>. */
+
+/* Based on glibc/sysdeps/<cpu>/{fpu_control.h,fenv_private.h,fenv_libc.h}. */
+
+#if (defined __x86_64__ || defined _M_X64) || (defined __i386 || defined _M_IX86)
+
+# if !(defined __x86_64__ || defined _M_X64)
+# if __GLIBC__ + (__GLIBC_MINOR__ >= 33) > 2
+/* glibc >= 2.33 has an API that tells us whether the CPU has an SSE unit. */
+# include <sys/platform/x86.h>
+# elif defined __sun
+/* Solaris has a global variable that tells us whether the CPU has an SSE unit. */
+extern int _sse_hw;
+# endif
+# endif
+
+/* CPU_HAS_SSE () returns true if the CPU has an SSE unit. */
+# if defined __x86_64__ || defined _M_X64
+# define CPU_HAS_SSE() 1
+# else
+# if __GLIBC__ + (__GLIBC_MINOR__ >= 33) > 2
+# define CPU_HAS_SSE() CPU_FEATURE_PRESENT (SSE)
+# elif defined __sun
+# define CPU_HAS_SSE() _sse_hw
+# else
+/* Otherwise, we assume that the SSE unit is present.
+ Only very old 32-bit processors, before Pentium 4, don't have it.
+ Don't bother testing it, through a 'cpuid' instruction. */
+# define CPU_HAS_SSE() 1
+# endif
+# endif
+
+/* fstat bits 5..2,0 indicate which floating-point exceptions have occurred
+ in the 387 compatible floating-point unit since the respective bit was last
+ set to zero.
+ mxcsr bits 5..2,0 indicate which floating-point exceptions have occurred
+ in the SSE floating-point unit since the respective bit was last set to
+ zero. */
+/* fctrl bits 5..2,0 indicate which floating-point exceptions shall, when
+ occurring in the 387 compatible floating-point unit, *not* trigger a trap
+ rather than merely set the corresponding bit in the fstat register.
+ mxcsr bits 12..9,7 indicate which floating-point exceptions shall, when
+ occurring in the SSE floating-point unit, *not* trigger a trap rather
+ than merely set the corresponding bit in the mxcsr register. */
+
+/* Macros that access the control word of the 387 unit, the so-called fctrl
+ register. */
+# define _FPU_GETCW(cw) __asm__ __volatile__ ("fnstcw %0" : "=m" (*&cw))
+# define _FPU_SETCW(cw) __asm__ __volatile__ ("fldcw %0" : : "m" (*&cw))
+
+/* Macros that access the status word of the 387 unit, the so-called fstat
+ register. */
+# define _FPU_GETSTAT(cw) __asm__ __volatile__ ("fnstsw %0" : "=m" (*&cw))
+
+/* Macros that access the control and status word of the SSE unit, the mxcsr
+ register. */
+# if defined __GNUC__ || defined __clang__
+# define _FPU_GETSSECW(cw) __asm__ __volatile__ ("stmxcsr %0" : "=m" (*&cw))
+# define _FPU_SETSSECW(cw) __asm__ __volatile__ ("ldmxcsr %0" : : "m" (*&cw))
+# elif defined _MSC_VER
+# include <mmintrin.h>
+/* Documentation:
+ <https://learn.microsoft.com/en-us/cpp/intrinsics/x86-intrinsics-list>
+ <https://learn.microsoft.com/en-us/cpp/intrinsics/x64-amd64-intrinsics-list>
+ <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_getcsr&ig_expand=3548>
+ <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_setcsr&ig_expand=5924>
+ */
+# define _FPU_GETSSECW(cw) ((cw) = _mm_getcsr ())
+# define _FPU_SETSSECW(cw) _mm_setcsr (cw)
+# endif
+
+/* The floating-point environment of the 387 unit. */
+typedef struct
+ {
+ /* 7 32-bit words: */
+ unsigned short __control_word; /* fctrl register */
+ unsigned short __reserved1;
+ unsigned short __status_word; /* fstat register */
+ unsigned short __reserved2;
+ unsigned int more[5];
+ }
+x86_387_fenv_t;
+
+# if defined _MSC_VER
+/* The MSVC header files have different values for the floating-point exceptions
+ than all the other platforms. Define some handy macros for conversion. */
+# define exceptions_to_x86hardware(exceptions) \
+ ( ((exceptions) & FE_INVALID ? 0x01 : 0) \
+ | ((exceptions) & FE_DIVBYZERO ? 0x04 : 0) \
+ | ((exceptions) & FE_OVERFLOW ? 0x08 : 0) \
+ | ((exceptions) & FE_UNDERFLOW ? 0x10 : 0) \
+ | ((exceptions) & FE_INEXACT ? 0x20 : 0))
+# define x86hardware_to_exceptions(fstat) \
+ ( ((fstat) & 0x01 ? FE_INVALID : 0) \
+ | ((fstat) & 0x04 ? FE_DIVBYZERO : 0) \
+ | ((fstat) & 0x08 ? FE_OVERFLOW : 0) \
+ | ((fstat) & 0x10 ? FE_UNDERFLOW : 0) \
+ | ((fstat) & 0x20 ? FE_INEXACT : 0))
+# else
+# define exceptions_to_x86hardware(exceptions) (exceptions)
+# define x86hardware_to_exceptions(fstat) (fstat)
+# endif
+
+/* When _MSC_VER is defined, the 387 compatible floating-point unit is *not*
+ in use. Only the SSE floating-point unit is used. This can be inferred
+ from two facts:
+ - sizeof (long double) == sizeof (double). That is, 'long double'
+ values are just 'double' values and can be processed in the SSE unit.
+ - After fegetenv (&env), the value of env._Fe_stat is *not* the fstat
+ register of the 387 unit. Rather, it is a artificial value. In
+ particular, (env._Fe_stat & 0x3f) is
+ == x86hardware_to_exceptions (_FPU_GETSSECW () & 0x3f). */
+
+#elif defined __aarch64__ /* arm64 */
+
+/* fpsr bits 4..0 indicate which floating-point exceptions have occurred
+ since the respective bit was last set to zero. */
+/* fpcr bits 12..8 indicate which floating-point exceptions shall, when
+ occurring, trigger a trap rather than merely set the corresponding bit
+ in the fpsr register. */
+
+# if __GNUC__ >= 6
+# define _FPU_GETCW(fpcr) (fpcr = __builtin_aarch64_get_fpcr ())
+# define _FPU_SETCW(fpcr) __builtin_aarch64_set_fpcr (fpcr)
+# else
+# define _FPU_GETCW(fpcr) \
+ __asm__ __volatile__ ("mrs %0, fpcr" : "=r" (fpcr))
+# define _FPU_SETCW(fpcr) \
+ __asm__ __volatile__ ("msr fpcr, %0" : : "r" (fpcr))
+# endif
+
+# if __GNUC__ >= 6
+# define _FPU_GETFPSR(fpsr) (fpsr = __builtin_aarch64_get_fpsr ())
+# define _FPU_SETFPSR(fpsr) __builtin_aarch64_set_fpsr (fpsr)
+# else
+# define _FPU_GETFPSR(fpsr) \
+ __asm__ __volatile__ ("mrs %0, fpsr" : "=r" (fpsr))
+# define _FPU_SETFPSR(fpsr) \
+ __asm__ __volatile__ ("msr fpsr, %0" : : "r" (fpsr))
+# endif
+
+#elif defined __arm__
+
+/* fpscr bits 23..22 indicate the rounding direction. */
+/* fpscr bits 4..0 indicate which floating-point exceptions have occurred
+ since the respective bit was last set to zero. */
+/* fpscr bits 12..8 indicate which floating-point exceptions shall, when
+ occurring, trigger a trap rather than merely set the corresponding bit
+ in the fpscr register. */
+
+# if !defined __SOFTFP__
+# define _FPU_GETCW(cw) \
+ __asm__ __volatile__ ("vmrs %0, fpscr" : "=r" (cw))
+# define _FPU_SETCW(cw) \
+ __asm__ __volatile__ ("vmsr fpscr, %0" : : "r" (cw))
+# endif
+
+#elif defined __alpha
+
+/* System calls. */
+extern unsigned long __ieee_get_fp_control (void);
+extern void __ieee_set_fp_control (unsigned long);
+
+# define _FPU_GETCW(fpcr) \
+ __asm__ __volatile__ ("excb; mf_fpcr %0" : "=f" (fpcr))
+# define _FPU_SETCW(fpcr) \
+ __asm__ __volatile__ ("mt_fpcr %0; excb" : : "f" (fpcr))
+
+#elif defined __hppa
+
+/* Bits 31..27 of the first 32-bit word of %fr0 indicate which floating-point
+ exceptions have occurred since the respective bit was last set to zero. */
+/* Bits 4..0 of the first 32-bit word of %fr0 indicate which floating-point
+ exceptions shall, when occurring, trigger a trap rather than merely set the
+ corresponding flag bit. */
+
+/* The status register is located in bits 0 to 31 of floating-point register 0. */
+# define _FPU_GETCW(cw) \
+({ \
+ union { __extension__ unsigned long long __fpreg; unsigned int __halfreg[2]; } __fullfp; \
+ /* Get the current status word. */ \
+ __asm__ ("fstd %%fr0,0(%1)\n\t" \
+ "fldd 0(%1),%%fr0\n\t" \
+ : "=m" (__fullfp.__fpreg) : "r" (&__fullfp.__fpreg) : "%r0"); \
+ cw = __fullfp.__halfreg[0]; \
+})
+# define _FPU_SETCW(cw) \
+({ \
+ union { __extension__ unsigned long long __fpreg; unsigned int __halfreg[2]; } __fullfp; \
+ /* Get the current status word and set the control word. */ \
+ __asm__ ("fstd %%fr0,0(%1)\n\t" \
+ : "=m" (__fullfp.__fpreg) : "r" (&__fullfp.__fpreg) : "%r0"); \
+ __fullfp.__halfreg[0] = cw; \
+ __asm__ ("fldd 0(%1),%%fr0\n\t" \
+ : : "m" (__fullfp.__fpreg), "r" (&__fullfp.__fpreg) : "%r0" ); \
+})
+
+#elif defined __ia64__
+
+/* fpsr bits 12..9,7 indicate which floating-point exceptions have occurred
+ since the respective bit was last set to zero. */
+/* fpsr bits 5..2,0 indicate which floating-point exceptions shall, when
+ occurring, *not* trigger a trap rather than merely set the corresponding
+ bit in the fpsr register. */
+
+# define _FPU_GETCW(fpsr) \
+ __asm__ __volatile__ ("mov.m %0=ar.fpsr" : "=r" (fpsr))
+# define _FPU_SETCW(fpsr) \
+ __asm__ __volatile__ ("mov.m ar.fpsr=%0" :: "r" (fpsr) : "memory")
+
+#elif defined __m68k__
+
+/* fpsr bits 7..3 indicate which floating-point exceptions have occurred
+ since the respective bit was last set to zero. */
+/* fpcr bits 15..8 indicate which floating-point exceptions shall, when
+ occurring, trigger a trap rather than merely set the corresponding bit
+ in the fpsr register:
+ - bit 15: branch/set on unordered
+ - bit 14: signaling not-a-number
+ - bit 13: operand error
+ - bit 12: overflow
+ - bit 11: underflow
+ - bit 10: divide by zero
+ - bit 9: inexact operation
+ - bit 8: inexact decimal input
+ FE_INVALID corresponds to all three: bit 15, bit 14, bit 13. */
+
+# define _FPU_GETCW(cw) __asm__ __volatile__ ("fmove%.l %!, %0" : "=dm" (cw))
+# define _FPU_SETCW(cw) __asm__ __volatile__ ("fmove%.l %0, %!" : : "dm" (cw))
+
+# define _FPU_GETFPSR(cw) __asm__ __volatile__ ("fmove%.l %/fpsr, %0" : "=dm" (cw))
+# define _FPU_SETFPSR(cw) __asm__ __volatile__ ("fmove%.l %0, %/fpsr" : : "dm" (cw))
+
+#elif defined __mips__
+
+/* fcsr bits 6..2 indicate which floating-point exceptions have occurred
+ since the respective bit was last set to zero.
+ fcsr bits 17..12 indicate which floating-point exceptions have occurred
+ in the most recent instruction. */
+/* fcsr bits 11..7 indicate which floating-point exceptions shall, when
+ occurring, trigger a trap rather than merely set the corresponding bit
+ in the fcsr register. */
+
+# define _FPU_GETCW(cw) __asm__ __volatile__ ("cfc1 %0,$31" : "=r" (cw))
+# define _FPU_SETCW(cw) __asm__ __volatile__ ("ctc1 %0,$31" : : "r" (cw))
+
+#elif defined __loongarch__
+
+/* fcsr0 bits 20..16 indicate which floating-point exceptions have occurred
+ since the respective bit was last set to zero.
+ fcsr0 bits 28..24 indicate which floating-point exceptions have occurred
+ in the most recent instruction. */
+/* fcsr0 bits 4..0 indicate which floating-point exceptions shall, when
+ occurring, trigger a trap rather than merely set the corresponding bit
+ in the fcsr0 register. */
+
+# define _FPU_GETCW(cw) __asm__ __volatile__ ("movfcsr2gr %0,$r0" : "=r" (cw))
+# define _FPU_SETCW(cw) __asm__ __volatile__ ("movgr2fcsr $r0,%0" : : "r" (cw))
+
+#elif defined __powerpc__
+
+/* fpscr bits 28..25 indicate which floating-point exceptions, other than
+ FE_INVALID, have occurred since the respective bit was last set to zero.
+ fpscr bits 24..19, 10..8 do the same thing, for various kinds of Invalid
+ Operation. fpscr bit 29 is the summary (the OR) of all these bits. */
+/* fpscr bits 7..3 indicate which floating-point exceptions shall, when
+ occurring, trigger a trap rather than merely set the corresponding bit
+ in the fpscr register. */
+
+# define _FPU_GETCW_AS_DOUBLE(cw) \
+ do { double env; __asm__ __volatile__ ("mffs %0" : "=f" (env)); cw = env; } \
+ while (0)
+# define _FPU_SETCW_AS_DOUBLE(cw) \
+ __asm__ __volatile__ ("mtfsf 0xff,%0" : : "f" (cw))
+
+#elif defined __riscv
+
+/* fcsr bits 4..0 indicate which floating-point exceptions have occurred
+ since the respective bit was last set to zero. */
+
+/* Trapping of floating-point exceptions does not work on RISC-V. That's
+ because the fcsr register has only bits for floating-point exception status,
+ but no bits for trapping floating-point exceptions. */
+
+#elif defined __s390__ || defined __s390x__
+
+/* fpc bits 23..19 indicate which floating-point exceptions have occurred
+ since the respective bit was last set to zero.
+ fpc bits 15..11 are part of the "data exception code" (DXC) and have a
+ similar meaning if bits 9..8 are both zero. */
+/* fpc bits 31..27 indicate which floating-point exceptions shall, when
+ occurring, trigger a trap rather than merely set the corresponding bit
+ in the fpc register. */
+
+# define _FPU_GETCW(cw) __asm__ __volatile__ ("efpc %0" : "=d" (cw))
+# define _FPU_SETCW(cw) __asm__ __volatile__ ("sfpc %0" : : "d" (cw))
+
+#elif defined __sh__
+
+/* fpscr bits 6..2 indicate which floating-point exceptions have occurred
+ since the respective bit was last set to zero. */
+/* fpscr bits 11..7 indicate which floating-point exceptions shall, when
+ occurring, trigger a trap rather than merely set the corresponding bit
+ in the fpscr register. */
+
+# define _FPU_GETCW(cw) __asm__ ("sts fpscr,%0" : "=r" (cw))
+# define _FPU_SETCW(cw) __asm__ ("lds %0,fpscr" : : "r" (cw))
+
+#elif defined __sparc
+
+/* fsr bits 9..5 indicate which floating-point exceptions have occurred
+ since the respective bit was last set to zero. */
+/* fsr bits 27..23 indicate which floating-point exceptions shall, when
+ occurring, trigger a trap rather than merely set the corresponding bit
+ in the fsr register. */
+
+# if defined __sparcv9 || defined __arch64__ /* sparc64 */
+# define _FPU_GETCW(X) __asm__ __volatile__ ("stx %%fsr,%0" : "=m" (X))
+# define _FPU_SETCW(X) __asm__ __volatile__ ("ldx %0,%%fsr" : : "m" (X))
+# else
+# define _FPU_GETCW(X) __asm__ __volatile__ ("st %%fsr,%0" : "=m" (X))
+# define _FPU_SETCW(X) __asm__ __volatile__ ("ld %0,%%fsr" : : "m" (X))
+# endif
+
+#endif
+
+#if defined _AIX && defined __powerpc__ /* AIX */
+
+/* <fpxcp.h> defines a type fpflag_t and macros FP_*. */
+
+/* Convert from an 'int exceptions' to an fpflag_t. */
+# if 0 /* Unoptimized */
+# define exceptions_to_fpflag(exceptions) \
+ ( ((exceptions) & FE_INVALID ? FP_INVALID : 0) \
+ | ((exceptions) & FE_DIVBYZERO ? FP_DIV_BY_ZERO : 0) \
+ | ((exceptions) & FE_OVERFLOW ? FP_OVERFLOW : 0) \
+ | ((exceptions) & FE_UNDERFLOW ? FP_UNDERFLOW : 0) \
+ | ((exceptions) & FE_INEXACT ? FP_INEXACT : 0))
+# else /* Optimized */
+# define exceptions_to_fpflag(exceptions) \
+ ((exceptions) & FE_ALL_EXCEPT)
+# endif
+
+/* Convert from an fpflag_t to an 'int exceptions'. */
+# if 0 /* Unoptimized */
+# define fpflag_to_exceptions(f) \
+ ( ((f) & FP_INVALID ? FE_INVALID : 0) \
+ | ((f) & FP_DIV_BY_ZERO ? FE_DIVBYZERO : 0) \
+ | ((f) & FP_OVERFLOW ? FE_OVERFLOW : 0) \
+ | ((f) & FP_UNDERFLOW ? FE_UNDERFLOW : 0) \
+ | ((f) & FP_INEXACT ? FE_INEXACT : 0))
+# else /* Optimized */
+# define fpflag_to_exceptions(f) \
+ ((f) & FE_ALL_EXCEPT)
+# endif
+
+/* The implementation of fegetexcept(). Avoids a module dependency. */
+# define fegetexcept_impl() \
+ ( (fp_is_enabled (TRP_INVALID) ? FE_INVALID : 0) \
+ | (fp_is_enabled (TRP_DIV_BY_ZERO) ? FE_DIVBYZERO : 0) \
+ | (fp_is_enabled (TRP_OVERFLOW) ? FE_OVERFLOW : 0) \
+ | (fp_is_enabled (TRP_UNDERFLOW) ? FE_UNDERFLOW : 0) \
+ | (fp_is_enabled (TRP_INEXACT) ? FE_INEXACT : 0))
+
+#endif
--- /dev/null
+/* Functions for controlling the floating-point rounding direction.
+ Copyright (C) 1997-2023 Free Software Foundation, Inc.
+
+ This file 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.
+
+ This file 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 this program. If not, see <https://www.gnu.org/licenses/>. */
+
+/* Based on glibc/sysdeps/<cpu>/{fegetround.c,fesetround.c}
+ together with glibc/sysdeps/<cpu>/{fpu_control.h,fenv_private.h,fenv_libc.h}. */
+
+#include <config.h>
+
+/* Specification. */
+#include <fenv.h>
+
+#include "fenv-private.h"
+
+#if defined __GNUC__ || defined __clang__ || defined _MSC_VER
+
+# if (defined __x86_64__ || defined _M_X64) || (defined __i386 || defined _M_IX86)
+
+int
+fegetround (void)
+{
+# ifdef _MSC_VER
+ /* XXX Simplify: access the SSE unit. */
+ fenv_t env;
+ if (fegetenv (&env) != 0)
+ return FE_TONEAREST;
+ unsigned int fctrl = env._Fe_ctl;
+# else
+ /* Use the rounding direction from the control word of the 387 unit, the
+ so-called fctrl register.
+ The rounding direction of the SSE unit, in the mxcsr register, is expected
+ to be in sync with that. */
+ unsigned short fctrl;
+ _FPU_GETCW (fctrl);
+# endif
+# ifdef _MSC_VER
+ /* The MSVC header files have different values for the rounding directions
+ than all the other platforms. Map
+ 0x0000 -> 0x0000 = FE_TONEAREST
+ 0x0400 -> 0x0200 = FE_DOWNWARD
+ 0x0800 -> 0x0100 = FE_UPWARD
+ 0x0C00 -> 0x0300 = FE_TOWARDZERO */
+ return ((fctrl & 0x0400) >> 1) | ((fctrl & 0x0800) >> 3);
+# else
+ return fctrl & 0x0C00;
+# endif
+}
+
+int
+fesetround (int rounding_direction)
+{
+# ifdef _MSC_VER
+ /* The MSVC header files have different values for the rounding directions
+ than all the other platforms. */
+ if ((rounding_direction & ~0x0300) != 0)
+ return -1;
+ /* Map
+ FE_TONEAREST = 0x0000 -> 0x0000
+ FE_DOWNWARD = 0x0200 -> 0x0400
+ FE_UPWARD = 0x0100 -> 0x0800
+ FE_TOWARDZERO = 0x0300 -> 0x0C00 */
+ rounding_direction =
+ ((rounding_direction & 0x0200) << 1) | ((rounding_direction & 0x0100) << 3);
+# else
+ if ((rounding_direction & ~0x0C00) != 0)
+ return -1;
+# endif
+
+ /* Set it in the 387 unit. */
+# ifdef _MSC_VER
+ /* XXX Simplify: only access the SSE unit. */
+ fenv_t env;
+ unsigned long orig_ctl;
+ if (fegetenv (&env) != 0)
+ return -1;
+ orig_ctl = env._Fe_ctl;
+ env._Fe_ctl = (env._Fe_ctl & ~0x0C00) | rounding_direction;
+ if (env._Fe_ctl != orig_ctl)
+ {
+ if (fesetenv (&env) != 0)
+ return -1;
+ }
+# else
+ unsigned short fctrl, orig_fctrl;
+ _FPU_GETCW (orig_fctrl);
+ fctrl = (orig_fctrl & ~0x0C00) | rounding_direction;
+ if (fctrl != orig_fctrl)
+ _FPU_SETCW (fctrl);
+# endif
+
+ if (CPU_HAS_SSE ())
+ {
+ /* Set it in the SSE unit as well. */
+ unsigned int mxcsr, orig_mxcsr;
+ _FPU_GETSSECW (orig_mxcsr);
+ mxcsr = (orig_mxcsr & ~(0x0C00 << 3)) | (rounding_direction << 3);
+ if (mxcsr != orig_mxcsr)
+ _FPU_SETSSECW (mxcsr);
+ }
+
+ return 0;
+}
+
+# elif defined __aarch64__ /* arm64 */
+
+int
+fegetround (void)
+{
+ unsigned long fpcr;
+ _FPU_GETCW (fpcr);
+ return (fpcr & 0x00C00000UL)
+# if FE_TOWARDZERO == 3 /* FreeBSD compatible FE_* values */
+ >> 22
+# endif
+ ;
+}
+
+int
+fesetround (int rounding_direction)
+{
+# if FE_TOWARDZERO == 3 /* FreeBSD compatible FE_* values */
+ if ((rounding_direction & ~3) != 0)
+ return -1;
+ rounding_direction = rounding_direction << 22;
+# else /* glibc compatible FE_* values */
+ if ((rounding_direction & ~0x00C00000UL) != 0)
+ return -1;
+# endif
+ unsigned long fpcr, orig_fpcr;
+ _FPU_GETCW (orig_fpcr);
+ fpcr = (orig_fpcr & ~0x00C00000UL) | rounding_direction;
+ if (fpcr != orig_fpcr)
+ _FPU_SETCW (fpcr);
+ return 0;
+}
+
+# elif defined __arm__
+
+int
+fegetround (void)
+{
+# ifdef __SOFTFP__
+ return FE_TONEAREST;
+# else
+ unsigned int fpscr;
+ _FPU_GETCW (fpscr);
+ return fpscr & 0x00C00000U;
+# endif
+}
+
+int
+fesetround (int rounding_direction)
+{
+# ifdef __SOFTFP__
+ if (rounding_direction != FE_TONEAREST)
+ return -1;
+# else
+ if ((rounding_direction & ~0x00C00000U) != 0)
+ return -1;
+ unsigned int fpscr, orig_fpscr;
+ _FPU_GETCW (orig_fpscr);
+ fpscr = (orig_fpscr & ~0x00C00000U) | rounding_direction;
+ if (fpscr != orig_fpscr)
+ _FPU_SETCW (fpscr);
+# endif
+ return 0;
+}
+
+# elif defined __alpha
+
+int
+fegetround (void)
+{
+ unsigned long fpcr;
+ _FPU_GETCW (fpcr);
+ return (fpcr >> 58) & 0x3UL;
+}
+
+int
+fesetround (int rounding_direction)
+{
+ if ((rounding_direction & ~0x3UL) != 0)
+ return -1;
+ unsigned long fpcr, orig_fpcr;
+ _FPU_GETCW (orig_fpcr);
+ fpcr = (orig_fpcr & ~(0x3UL << 58))
+ | ((unsigned long) rounding_direction << 58);
+ if (fpcr != orig_fpcr)
+ _FPU_SETCW (fpcr);
+ return 0;
+}
+
+# elif defined __hppa
+
+int
+fegetround (void)
+{
+ unsigned int fpstatus;
+ _FPU_GETCW (fpstatus);
+ return fpstatus & 0x00000600U;
+}
+
+int
+fesetround (int rounding_direction)
+{
+ if ((rounding_direction & ~0x00000600U) != 0)
+ return -1;
+ unsigned int fpstatus, orig_fpstatus;
+ _FPU_GETCW (orig_fpstatus);
+ fpstatus = (orig_fpstatus & ~0x00000600U) | rounding_direction;
+ if (fpstatus != orig_fpstatus)
+ _FPU_SETCW (fpstatus);
+ return 0;
+}
+
+# elif defined __ia64__
+
+int
+fegetround (void)
+{
+ unsigned long fpsr;
+ _FPU_GETCW (fpsr);
+ return (fpsr >> 10) & 0x3UL;
+}
+
+int
+fesetround (int rounding_direction)
+{
+ if ((rounding_direction & ~0x3UL) != 0)
+ return -1;
+ unsigned long fpsr, orig_fpsr;
+ _FPU_GETCW (orig_fpsr);
+ fpsr = (orig_fpsr & ~(0x3UL << 10))
+ | ((unsigned long) rounding_direction << 10);
+ if (fpsr != orig_fpsr)
+ _FPU_SETCW (fpsr);
+ return 0;
+}
+
+# elif defined __m68k__
+
+int
+fegetround (void)
+{
+ unsigned int fpcr;
+ _FPU_GETCW (fpcr);
+ return fpcr & 0x30U;
+}
+
+int
+fesetround (int rounding_direction)
+{
+ if ((rounding_direction & ~0x30U) != 0)
+ return -1;
+ unsigned int fpcr, orig_fpcr;
+ _FPU_GETCW (orig_fpcr);
+ fpcr = (orig_fpcr & ~0x30U) | rounding_direction;
+ if (fpcr != orig_fpcr)
+ _FPU_SETCW (fpcr);
+ return 0;
+}
+
+# elif defined __mips__
+
+int
+fegetround (void)
+{
+ unsigned int fcsr;
+ _FPU_GETCW (fcsr);
+ return fcsr & 0x3U;
+}
+
+int
+fesetround (int rounding_direction)
+{
+ if ((rounding_direction & ~0x3U) != 0)
+ return -1;
+ unsigned int fcsr, orig_fcsr;
+ _FPU_GETCW (orig_fcsr);
+ fcsr = (orig_fcsr & ~0x3U) | rounding_direction;
+ if (fcsr != orig_fcsr)
+ _FPU_SETCW (fcsr);
+ return 0;
+}
+
+# elif defined __loongarch__
+
+int
+fegetround (void)
+{
+ unsigned int fcsr;
+ _FPU_GETCW (fcsr);
+ return fcsr & 0x300U;
+}
+
+int
+fesetround (int rounding_direction)
+{
+ if ((rounding_direction & ~0x300U) != 0)
+ return -1;
+ unsigned int fcsr, orig_fcsr;
+ _FPU_GETCW (orig_fcsr);
+ fcsr = (orig_fcsr & ~0x300U) | rounding_direction;
+ if (fcsr != orig_fcsr)
+ _FPU_SETCW (fcsr);
+ return 0;
+}
+
+# elif defined __powerpc__
+
+/* The AIX header files have different values for the rounding directions
+ than all the other platforms: The values 0 and 1 are swapped.
+ (They probably did this in order to have a trivial FLT_ROUNDS macro, cf.
+ <https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/float.h.html>.)
+ Define some handy macros for conversion. */
+# ifdef _AIX
+# define fe_to_hardware(x) ((x) ^ ((x) < 2))
+# define hardware_to_fe(x) ((x) ^ ((x) < 2))
+# else
+# define fe_to_hardware(x) (x)
+# define hardware_to_fe(x) (x)
+# endif
+
+int
+fegetround (void)
+{
+# if 1
+ unsigned int result;
+ __asm__ __volatile__ ("mcrfs 7,7 ; mfcr %0" : "=r" (result) : : "cr7");
+ return hardware_to_fe (result & 3);
+# else
+ union { unsigned long long u; double f; } memenv;
+ _FPU_GETCW_AS_DOUBLE (memenv.f);
+ return hardware_to_fe (memenv.u & 3);
+# endif
+}
+
+int
+fesetround (int rounding_direction)
+{
+ if ((rounding_direction & ~0x3U) != 0)
+ return -1;
+# ifdef _AIX
+ rounding_direction = fe_to_hardware (rounding_direction);
+# endif
+ if (rounding_direction & 2)
+ __asm__ __volatile__ ("mtfsb1 30");
+ else
+ __asm__ __volatile__ ("mtfsb0 30");
+ if (rounding_direction & 1)
+ __asm__ __volatile__ ("mtfsb1 31");
+ else
+ __asm__ __volatile__ ("mtfsb0 31");
+ return 0;
+}
+
+# elif defined __riscv
+
+int
+fegetround (void)
+{
+ int rounding_direction;
+ __asm__ __volatile__ ("frrm %0" : "=r" (rounding_direction));
+# if FE_UPWARD == 0x60 /* FreeBSD compatible FE_* values */
+ return rounding_direction << 5;
+# else
+ return rounding_direction;
+# endif
+}
+
+int
+fesetround (int rounding_direction)
+{
+# if FE_UPWARD == 0x60 /* FreeBSD compatible FE_* values */
+ if ((rounding_direction & ~0x60) != 0)
+ return -1;
+ rounding_direction = rounding_direction >> 5;
+# else
+ if ((rounding_direction & ~3) != 0)
+ return -1;
+# endif
+ __asm__ __volatile__ ("fsrm %z0" : : "rJ" (rounding_direction));
+ return 0;
+}
+
+# elif defined __s390__ || defined __s390x__
+
+int
+fegetround (void)
+{
+ unsigned int fpc;
+ _FPU_GETCW (fpc);
+ return fpc & 0x3U;
+}
+
+int
+fesetround (int rounding_direction)
+{
+ if ((rounding_direction & ~0x3U) != 0)
+ return -1;
+# if 1
+ __asm__ __volatile__ ("srnm 0(%0)" : : "a" (rounding_direction));
+# else
+ unsigned int fpc, orig_fpc;
+ _FPU_GETCW (orig_fpc);
+ fpc = (orig_fpc & ~0x3U) | rounding_direction;
+ if (fpc != orig_fpc)
+ _FPU_SETCW (fpc);
+# endif
+ return 0;
+}
+
+# elif defined __sh__
+
+int
+fegetround (void)
+{
+ unsigned int fpscr;
+ _FPU_GETCW (fpscr);
+ return fpscr & 0x1U;
+}
+
+int
+fesetround (int rounding_direction)
+{
+ if ((rounding_direction & ~0x1U) != 0)
+ return -1;
+ unsigned int fpscr, orig_fpscr;
+ _FPU_GETCW (orig_fpscr);
+ fpscr = (orig_fpscr & ~0x1U) | rounding_direction;
+ if (fpscr != orig_fpscr)
+ _FPU_SETCW (fpscr);
+ return 0;
+}
+
+# elif defined __sparc
+
+int
+fegetround (void)
+{
+ unsigned long fsr;
+ _FPU_GETCW (fsr);
+ return (fsr & 0xC0000000UL)
+# if FE_DOWNWARD == 3 /* FreeBSD compatible FE_* values */
+ >> 30
+# endif
+ ;
+}
+
+int
+fesetround (int rounding_direction)
+{
+# if FE_DOWNWARD == 3 /* FreeBSD compatible FE_* values */
+ if ((rounding_direction & ~3) != 0)
+ return -1;
+ rounding_direction = (unsigned int) rounding_direction << 30;
+# else /* glibc compatible FE_* values */
+ if (((unsigned int) rounding_direction & ~0xC0000000UL) != 0)
+ return -1;
+# endif
+ unsigned long fsr, orig_fsr;
+ _FPU_GETCW (orig_fsr);
+ fsr = (orig_fsr & ~0xC0000000UL) | (unsigned int) rounding_direction;
+ if (fsr != orig_fsr)
+ _FPU_SETCW (fsr);
+ return 0;
+}
+
+# else
+
+# if defined __GNUC__ || defined __clang__
+# warning "Unknown CPU / architecture. Please report your platform and compiler to <bug-gnulib@gnu.org>."
+# endif
+# define NEED_FALLBACK 1
+
+# endif
+
+#else
+
+/* The compiler does not support __asm__ statements or equivalent
+ intrinsics. */
+
+# if HAVE_FPSETROUND
+/* FreeBSD ≥ 3.1, NetBSD ≥ 1.1, OpenBSD, IRIX, Solaris, Minix ≥ 3.2. */
+
+/* Get fpgetround, fpsetround. */
+# include <ieeefp.h>
+
+int
+fegetround (void)
+{
+ return fpgetround ();
+}
+
+int
+fesetround (int rounding_direction)
+{
+ fpsetround (rounding_direction);
+ return 0;
+}
+
+# elif defined _AIX /* AIX */
+
+/* Get fp_read_rnd, fp_swap_rnd. */
+# include <float.h>
+
+/* Documentation:
+ <https://www.ibm.com/docs/en/aix/7.3?topic=f-fp-read-rnd-fp-swap-rnd-subroutine> */
+
+int
+fegetround (void)
+{
+ return fp_read_rnd ();
+}
+
+int
+fesetround (int rounding_direction)
+{
+ fp_swap_rnd (rounding_direction);
+ return 0;
+}
+
+# else
+
+# define NEED_FALLBACK 1
+
+# endif
+
+#endif
+
+#if NEED_FALLBACK
+
+/* A dummy fallback. */
+
+# include <float.h>
+# include <stdlib.h>
+
+int
+fegetround (void)
+{
+ /* Cf. <https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/float.h.html> */
+ switch (FLT_ROUNDS)
+ {
+ case 0: return FE_TOWARDZERO;
+ case 1: return FE_TONEAREST;
+ case 2: return FE_UPWARD;
+ case 3: return FE_DOWNWARD;
+ default: abort ();
+ }
+}
+
+int
+fesetround (int rounding_direction)
+{
+ if (rounding_direction != fegetround ())
+ return -1;
+ return 0;
+}
+
+#endif
#error "Please include config.h first."
#endif
+/* The definitions of _GL_FUNCDECL_RPL etc. are copied here. */
+
/* The definition of _GL_WARN_ON_USE is copied here. */
# elif defined __aarch64__ /* arm64 */
+/* Attention: FreeBSD libc has these values shifted right by 22 bits! */
# define FE_TONEAREST (0 << 22)
# define FE_UPWARD (1 << 22)
# define FE_DOWNWARD (2 << 22)
# elif defined __riscv
+/* Attention: FreeBSD libc has these values shifted left by 5 bits! */
# define FE_TONEAREST 0
# define FE_TOWARDZERO 1
# define FE_DOWNWARD 2
#endif
+#if @GNULIB_FEGETROUND@
+/* Returns the current rounding direction. */
+# if @REPLACE_FEGETROUND@ || (!@HAVE_FEGETROUND@ && (defined __GLIBC__ || defined __FreeBSD__)) /* has an inline definition */
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef fegetround
+# define fegetround rpl_fegetround
+# endif
+_GL_FUNCDECL_RPL (fegetround, int, (void));
+_GL_CXXALIAS_RPL (fegetround, int, (void));
+# else
+# if !@HAVE_FEGETROUND@
+_GL_FUNCDECL_SYS (fegetround, int, (void));
+# endif
+_GL_CXXALIAS_SYS (fegetround, int, (void));
+# endif
+_GL_CXXALIASWARN (fegetround);
+#endif
+
+#if @GNULIB_FESETROUND@
+/* Sets the rounding direction of the current thread.
+ Returns zero if the argument is valid and the operation was thus successful.
+ Returns non-zero upon failure. */
+# if @REPLACE_FESETROUND@ || (!@HAVE_FESETROUND@ && defined __FreeBSD__) /* has an inline definition */
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef fesetround
+# define fesetround rpl_fesetround
+# endif
+_GL_FUNCDECL_RPL (fesetround, int, (int rounding_direction));
+_GL_CXXALIAS_RPL (fesetround, int, (int rounding_direction));
+# else
+# if !@HAVE_FESETROUND@
+_GL_FUNCDECL_SYS (fesetround, int, (int rounding_direction));
+# endif
+_GL_CXXALIAS_SYS (fesetround, int, (int rounding_direction));
+# endif
+_GL_CXXALIASWARN (fesetround);
+#endif
+
/* ISO C 99 § 7.6.2 Floating-point exceptions
ISO C 23 § 7.6.4 Floating-point exceptions
--- /dev/null
+# fenv-rounding.m4 serial 1
+dnl Copyright (C) 2023 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_FENV_ROUNDING],
+[
+ AC_REQUIRE([gl_FENV_H_DEFAULTS])
+
+ dnl On FreeBSD 11/i386, fesetround needs -lm while fegetround doesn't.
+ gl_MATHFUNC([fesetround], [int], [(int)], [#include <fenv.h>])
+ if test $gl_cv_func_fesetround_no_libm = yes \
+ || test $gl_cv_func_fesetround_in_libm = yes; then
+ dnl The fesetround function does not work on MSVC 14.
+ AC_CACHE_CHECK([whether fesetround works],
+ [gl_cv_func_fesetround_works],
+ [case "$host_os" in
+ # Guess no on native Windows other than mingw.
+ mingw* | windows*)
+ AC_EGREP_CPP([Problem], [
+#ifndef __MINGW32__
+ Problem
+#endif
+ ],
+ [gl_cv_func_fesetround_works="guessing no"],
+ [gl_cv_func_fesetround_works="guessing yes"])
+ ;;
+ *) gl_cv_func_fesetround_works="guessing yes" ;;
+ esac
+ ])
+ case "$gl_cv_func_fesetround_works" in
+ *yes) ;;
+ *) REPLACE_FESETROUND=1 ;;
+ esac
+ if test $REPLACE_FESETROUND = 1; then
+ dnl One more function is defined in the same compilation unit.
+ REPLACE_FEGETROUND=1
+ FENV_ROUNDING_LIBM=
+ else
+ dnl It needs linking with -lm on
+ dnl glibc, FreeBSD, NetBSD, OpenBSD, AIX, HP-UX, IRIX, Solaris, Android.
+ if test $gl_cv_func_fesetround_no_libm = yes; then
+ FENV_ROUNDING_LIBM=
+ else
+ FENV_ROUNDING_LIBM=-lm
+ fi
+ fi
+ else
+ HAVE_FEGETROUND=0
+ HAVE_FESETROUND=0
+ FENV_ROUNDING_LIBM=
+ fi
+ if test $HAVE_FESETROUND = 0 || test $REPLACE_FESETROUND = 1; then
+ gl_PREREQ_FENV_ROUNDING
+ dnl Possibly need -lm for fpgetround(), fpsetround().
+ if test $gl_cv_func_fpsetround_no_libm = no \
+ && test $gl_cv_func_fpsetround_in_libm = yes \
+ && test -z "$FENV_ROUNDING_LIBM"; then
+ FENV_ROUNDING_LIBM=-lm
+ fi
+ fi
+ AC_SUBST([FENV_ROUNDING_LIBM])
+])
+
+dnl Prerequisites of lib/fenv-round.c.
+AC_DEFUN([gl_PREREQ_FENV_ROUNDING],
+[
+ gl_MATHFUNC([fpsetround], [fp_rnd], [(fp_rnd)],
+ [#include <ieeefp.h>
+ ])
+ if test $gl_cv_func_fpsetround_no_libm = yes \
+ || test $gl_cv_func_fpsetround_in_libm = yes; then
+ AC_DEFINE([HAVE_FPSETROUND], [1],
+ [Define to 1 if you have the 'fpsetround' function.])
+ fi
+])
-# mathfunc.m4 serial 14
+# mathfunc.m4 serial 15
dnl Copyright (C) 2010-2023 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
[m4_bpatsubst(
[m4_bpatsubst(
[m4_bpatsubst(
- [$3],
- [int\( const\)? \*],
- [&i_ret])],
- [float\( const\)? \*], [&f_ret])],
- [double\( const\)? \*], [&d_ret])],
- [long double\( const\)? \*], [&l_ret])],
+ [m4_bpatsubst(
+ [$3],
+ [int\( const\)? \*],
+ [&i_ret])],
+ [float\( const\)? \*], [&f_ret])],
+ [double\( const\)? \*], [&d_ret])],
+ [long double\( const\)? \*], [&l_ret])],
+ [fp_rnd], [1])],
[fp_except_t], [1])],
[int], [2])],
[float], [1.618034f])],
Depends-on:
gen-header
include_next
+snippet/c++defs
snippet/warn-on-use
configure.ac:
BUILT_SOURCES += fenv.h
# We need the following in order to create an override of <fenv.h>.
-fenv.h: fenv.in.h $(top_builddir)/config.status $(WARN_ON_USE_H)
+fenv.h: fenv.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(WARN_ON_USE_H)
@NMD@ $(AM_V_GEN)$(MKDIR_P) '%reldir%'
$(gl_V_at)$(SED_HEADER_STDOUT) \
-e 's|@''GUARD_PREFIX''@|${gl_include_guard_prefix}|g' \
-e 's|@''REPLACE_FESETEXCEPTFLAG''@|$(REPLACE_FESETEXCEPTFLAG)|g' \
-e 's|@''REPLACE_FESETROUND''@|$(REPLACE_FESETROUND)|g' \
-e 's|@''REPLACE_FETESTEXCEPT''@|$(REPLACE_FETESTEXCEPT)|g' \
+ -e '/definitions of _GL_FUNCDECL_RPL/r $(CXXDEFS_H)' \
-e '/definition of _GL_WARN_ON_USE/r $(WARN_ON_USE_H)' \
$(srcdir)/fenv.in.h > $@-t
$(AM_V_at)mv $@-t $@
--- /dev/null
+Description:
+Functions for controlling the floating-point rounding direction:
+fegetround, fesetround.
+
+Files:
+lib/fenv-round.c
+lib/fenv-private.h
+m4/fenv-rounding.m4
+m4/mathfunc.m4
+
+Depends-on:
+fenv
+
+configure.ac:
+gl_FENV_ROUNDING
+gl_CONDITIONAL([GL_COND_OBJ_FENV_ROUNDING],
+ [test $HAVE_FESETROUND = 0 || test $REPLACE_FESETROUND = 1])
+gl_FENV_MODULE_INDICATOR([fegetround])
+gl_FENV_MODULE_INDICATOR([fesetround])
+
+Makefile.am:
+if GL_COND_OBJ_FENV_ROUNDING
+lib_SOURCES += fenv-round.c
+endif
+
+Include:
+#include <fenv.h>
+
+Link:
+$(FENV_ROUNDING_LIBM)
+
+License:
+LGPLv2+
+
+Maintainer:
+all