This new module implements the ISO C 99 functions for saving and restoring the
floating-point exception flags.

The unit tests uncovered bugs in glibc, musl libc (*), macOS, AIX, mingw.

(*) The one on musl libc is a documented limitation.


2023-10-30  Bruno Haible  <br...@clisp.org>

        fenv-exceptions-state-c99: Add tests.
        * tests/test-fenv-except-state-1.c: New file.
        * tests/test-fenv-except-state-2.c: New file.
        * modules/fenv-exceptions-state-c99-tests: New file.

        fenv-exceptions-state-c99: New module.
        * lib/fenv.in.h (fegetexceptflag, fesetexceptflag): New declarations.
        * lib/fenv-except-state-get.c: New file, baed on glibc.
        * lib/fenv-except-state-set.c: New file, baed on glibc.
        * m4/mathfunc.m4 (gl_MATHFUNC): Handle also the 'fexcept_t *' type.
        * m4/fenv-exceptions-state.m4: New file.
        * modules/fenv-exceptions-state-c99: New file.
        * doc/posix-functions/fegetexceptflag.texi: Mention the new module.
        * doc/posix-functions/fesetexceptflag.texi: Mention the new module and
        the glibc, musl libc, macOS, AIX, mingw bugs.

From ee6460d256b0ba048ef5ecf39f028b10c9986b12 Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Mon, 30 Oct 2023 16:39:19 +0100
Subject: [PATCH 1/2] fenv-exceptions-state-c99: New module.

* lib/fenv.in.h (fegetexceptflag, fesetexceptflag): New declarations.
* lib/fenv-except-state-get.c: New file, baed on glibc.
* lib/fenv-except-state-set.c: New file, baed on glibc.
* m4/mathfunc.m4 (gl_MATHFUNC): Handle also the 'fexcept_t *' type.
* m4/fenv-exceptions-state.m4: New file.
* modules/fenv-exceptions-state-c99: New file.
* doc/posix-functions/fegetexceptflag.texi: Mention the new module.
* doc/posix-functions/fesetexceptflag.texi: Mention the new module and
the glibc, musl libc, macOS, AIX, mingw bugs.
---
 ChangeLog                                |  13 +
 doc/posix-functions/fegetexceptflag.texi |   8 +-
 doc/posix-functions/fesetexceptflag.texi |  23 +-
 lib/fenv-except-state-get.c              | 353 ++++++++++++++
 lib/fenv-except-state-set.c              | 597 +++++++++++++++++++++++
 lib/fenv.in.h                            |  49 +-
 m4/fenv-exceptions-state.m4              | 221 +++++++++
 m4/mathfunc.m4                           |   6 +-
 modules/fenv-exceptions-state-c99        |  43 ++
 9 files changed, 1302 insertions(+), 11 deletions(-)
 create mode 100644 lib/fenv-except-state-get.c
 create mode 100644 lib/fenv-except-state-set.c
 create mode 100644 m4/fenv-exceptions-state.m4
 create mode 100644 modules/fenv-exceptions-state-c99

diff --git a/ChangeLog b/ChangeLog
index 1fc40e4ae2..291e89ec60 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2023-10-30  Bruno Haible  <br...@clisp.org>
+
+	fenv-exceptions-state-c99: New module.
+	* lib/fenv.in.h (fegetexceptflag, fesetexceptflag): New declarations.
+	* lib/fenv-except-state-get.c: New file, baed on glibc.
+	* lib/fenv-except-state-set.c: New file, baed on glibc.
+	* m4/mathfunc.m4 (gl_MATHFUNC): Handle also the 'fexcept_t *' type.
+	* m4/fenv-exceptions-state.m4: New file.
+	* modules/fenv-exceptions-state-c99: New file.
+	* doc/posix-functions/fegetexceptflag.texi: Mention the new module.
+	* doc/posix-functions/fesetexceptflag.texi: Mention the new module and
+	the glibc, musl libc, macOS, AIX, mingw bugs.
+
 2023-10-30  Bruno Haible  <br...@clisp.org>
 
 	fenv-exceptions-tracking-{c99,c23}: Fix the x86_64 and i386 case.
diff --git a/doc/posix-functions/fegetexceptflag.texi b/doc/posix-functions/fegetexceptflag.texi
index 5b45fe8d26..58915e0530 100644
--- a/doc/posix-functions/fegetexceptflag.texi
+++ b/doc/posix-functions/fegetexceptflag.texi
@@ -4,15 +4,15 @@
 
 POSIX specification:@* @url{https://pubs.opengroup.org/onlinepubs/9699919799/functions/fegetexceptflag.html}
 
-Gnulib module: ---
+Gnulib module: fenv-exceptions-state-c99
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on some platforms:
+FreeBSD 6.0, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.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, Minix 3.1.8, AIX 5.1, IRIX 6.5, Solaris 9, Cygwin 1.7.7, MSVC 9, Android 4.4.
 @end itemize
diff --git a/doc/posix-functions/fesetexceptflag.texi b/doc/posix-functions/fesetexceptflag.texi
index 5258d35099..66075be675 100644
--- a/doc/posix-functions/fesetexceptflag.texi
+++ b/doc/posix-functions/fesetexceptflag.texi
@@ -4,15 +4,30 @@
 
 POSIX specification:@* @url{https://pubs.opengroup.org/onlinepubs/9699919799/functions/fesetexceptflag.html}
 
-Gnulib module: ---
+Gnulib module: fenv-exceptions-state-c99
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on some platforms:
+FreeBSD 5.2.1, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 5.1, IRIX 6.5, Solaris 9, Cygwin 1.7.7, MSVC 9, Android 4.4.
+@item
+This function triggers floating-point exception traps although it shouldn't, on
+@c https://sourceware.org/bugzilla/show_bug.cgi?id=30990
+glibc 2.37/i386, glibc 2.37/x86_64,
+@c https://sourceware.org/bugzilla/show_bug.cgi?id=30988
+glibc 2.37/powerpc,
+musl libc, Mac OS X 10.5, mingw.
+@item
+This function clears too many floating-point exception flags on
+@c https://sourceware.org/bugzilla/show_bug.cgi?id=30998
+glibc 2.37/alpha.
+@item
+This function is also buggy on
+@c Without the override, the unit test test-fenv-except-state-1 fails.
+AIX 7.3.1.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This function is missing on some platforms:
-FreeBSD 5.2.1, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 5.1, IRIX 6.5, Solaris 9, Cygwin 1.7.7, MSVC 9, Android 4.4.
 @end itemize
diff --git a/lib/fenv-except-state-get.c b/lib/fenv-except-state-get.c
new file mode 100644
index 0000000000..0e22e7f7c8
--- /dev/null
+++ b/lib/fenv-except-state-get.c
@@ -0,0 +1,353 @@
+/* Functions for saving the floating-point exception status flags.
+   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>/{fgetexcptflg.c,fsetexcptflg.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)
+
+/* On most OSes, fexcept_t is binary-equivalent to an 'unsigned short'.
+   On NetBSD, OpenBSD, Solaris, Cygwin, MSVC, Android/x86_64, Minix, fexcept_t
+   is equivalent to an 'unsigned int'.
+   A simple C cast does the necessary conversion.  */
+
+int
+fegetexceptflag (fexcept_t *saved_flags, int exceptions)
+{
+  /* Just like fetestexcept.  */
+#  if defined _MSC_VER
+
+  unsigned int mxcsr;
+  _FPU_GETSSECW (mxcsr);
+  *saved_flags = x86hardware_to_exceptions (mxcsr) & FE_ALL_EXCEPT & exceptions;
+
+#  else
+
+  unsigned short fstat;
+  _FPU_GETSTAT (fstat);
+
+  unsigned int mxcsr = 0;
+  if (CPU_HAS_SSE ())
+    {
+      /* Look at the flags in the SSE unit as well.  */
+      _FPU_GETSSECW (mxcsr);
+    }
+
+  *saved_flags = x86hardware_to_exceptions (fstat | mxcsr)
+                 & FE_ALL_EXCEPT & exceptions;
+#  endif
+
+  return 0;
+}
+
+# elif defined __aarch64__ /* arm64 */
+
+/* On Linux, NetBSD, and Android, fexcept_t is binary-equivalent to
+   an 'unsigned int'.
+   On macOS, fexcept_t is binary-equivalent to an 'unsigned short'.
+   On FreeBSD and OpenBSD, fexcept_t is binary-equivalent to an 'unsigned long'.
+   A simple C cast does the necessary conversion.  */
+
+int
+fegetexceptflag (fexcept_t *saved_flags, int exceptions)
+{
+  /* Just like fetestexcept.  */
+  unsigned long fpsr;
+  _FPU_GETFPSR (fpsr);
+  *saved_flags = fpsr & FE_ALL_EXCEPT & exceptions;
+  return 0;
+}
+
+# elif defined __arm__
+
+/* On all OSes, fexcept_t is binary-equivalent to an 'unsigned int'.  */
+
+int
+fegetexceptflag (fexcept_t *saved_flags, int exceptions)
+{
+  /* Just like fetestexcept.  */
+#  ifdef __SOFTFP__
+  return -1;
+#  else
+  unsigned int fpscr;
+  _FPU_GETCW (fpscr);
+  *saved_flags = fpscr & FE_ALL_EXCEPT & exceptions;
+  return 0;
+#  endif
+}
+
+# elif defined __alpha
+
+/* On all OSes except NetBSD and OpenBSD, fexcept_t is binary-equivalent to
+   an 'unsigned long'.
+   On NetBSD, it is equivalent to an 'unsigned short'.
+   On OpenBSD, it is equivalent to an 'unsigned int'.
+   A simple C cast does the necessary conversion.  */
+
+int
+fegetexceptflag (fexcept_t *saved_flags, int exceptions)
+{
+  /* Just like fetestexcept.  */
+  unsigned long swcr = __ieee_get_fp_control ();
+  *saved_flags = swcr & FE_ALL_EXCEPT & exceptions;
+  return 0;
+}
+
+# elif defined __hppa
+
+/* On all OSes, fexcept_t is binary-equivalent to an 'unsigned int'.  */
+
+int
+fegetexceptflag (fexcept_t *saved_flags, int exceptions)
+{
+  /* Just like fetestexcept.  */
+  union { unsigned long long fpreg; unsigned int halfreg[2]; } s;
+  /* Get the current status word. */
+  __asm__ __volatile__ ("fstd %%fr0,0(%1)" : "=m" (s.fpreg) : "r" (&s.fpreg) : "%r0");
+  *saved_flags = (s.halfreg[0] >> 27) & FE_ALL_EXCEPT & exceptions;
+  return 0;
+}
+
+# elif defined __ia64__
+
+/* On all OSes except NetBSD, fexcept_t is binary-equivalent to
+   an 'unsigned long'.
+   On NetBSD, it is equivalent to an 'unsigned short'.
+   A simple C cast does the necessary conversion.  */
+
+int
+fegetexceptflag (fexcept_t *saved_flags, int exceptions)
+{
+  /* Just like fetestexcept.  */
+  unsigned long fpsr;
+  _FPU_GETCW (fpsr);
+  *saved_flags = (fpsr >> 13) & FE_ALL_EXCEPT & exceptions;
+  return 0;
+}
+
+# elif defined __m68k__
+
+/* On all OSes, fexcept_t is binary-equivalent to an 'unsigned int'.  */
+
+int
+fegetexceptflag (fexcept_t *saved_flags, int exceptions)
+{
+  /* Just like fetestexcept.  */
+  unsigned int fpsr;
+  _FPU_GETFPSR (fpsr);
+  *saved_flags = fpsr & FE_ALL_EXCEPT & exceptions;
+  return 0;
+}
+
+# elif defined __mips__
+
+/* On all OSes except NetBSD and OpenBSD, fexcept_t is binary-equivalent to
+   an 'unsigned short'.
+   On NetBSD and OpenBSD, it is equivalent to an 'unsigned int'.
+   A simple C cast does the necessary conversion.  */
+
+int
+fegetexceptflag (fexcept_t *saved_flags, int exceptions)
+{
+  /* Just like fetestexcept.  */
+  unsigned int fcsr;
+  _FPU_GETCW (fcsr);
+  *saved_flags = fcsr & FE_ALL_EXCEPT & exceptions;
+  return 0;
+}
+
+# elif defined __loongarch__
+
+/* On all OSes, fexcept_t is binary-equivalent to an 'unsigned int'.  */
+
+int
+fegetexceptflag (fexcept_t *saved_flags, int exceptions)
+{
+  /* Just like fetestexcept.  */
+  unsigned int fcsr;
+  _FPU_GETCW (fcsr);
+  *saved_flags = fcsr & FE_ALL_EXCEPT & exceptions;
+  return 0;
+}
+
+# elif defined __powerpc__
+
+/* On all OSes, fexcept_t is binary-equivalent to an 'unsigned int'.  */
+
+int
+fegetexceptflag (fexcept_t *saved_flags, int exceptions)
+{
+  /* Just like fetestexcept.  */
+  union { unsigned long long u; double f; } memenv;
+  _FPU_GETCW_AS_DOUBLE (memenv.f);
+  *saved_flags = memenv.u & FE_ALL_EXCEPT & exceptions;
+  return 0;
+}
+
+# elif defined __riscv
+
+/* On all OSes except FreeBSD, fexcept_t is binary-equivalent to
+   an 'unsigned int'.
+   On FreeBSD, it is equivalent to an 'unsigned long'.
+   A simple C cast does the necessary conversion.  */
+
+int
+fegetexceptflag (fexcept_t *saved_flags, int exceptions)
+{
+  /* Just like fetestexcept.  */
+  unsigned int flags;
+  __asm__ __volatile__ ("frflags %0" : "=r" (flags)); /* same as "csrr %0, fflags" */
+  *saved_flags = flags & FE_ALL_EXCEPT & exceptions;
+  return 0;
+}
+
+# elif defined __s390__ || defined __s390x__
+
+/* On all OSes, fexcept_t is binary-equivalent to an 'unsigned int'.  */
+
+int
+fegetexceptflag (fexcept_t *saved_flags, int exceptions)
+{
+  /* Just like fetestexcept.  */
+  unsigned int fpc;
+  _FPU_GETCW (fpc);
+#  if FE_INEXACT == 8 /* glibc compatible FE_* values */
+  *saved_flags = ((fpc >> 16) | ((fpc & 0x00000300) == 0 ? fpc >> 8 : 0))
+                 & FE_ALL_EXCEPT & exceptions;
+#  else /* musl libc compatible FE_* values */
+  *saved_flags = (fpc | ((fpc & 0x00000300) == 0 ? fpc << 8 : 0))
+                 & FE_ALL_EXCEPT & exceptions;
+#  endif
+  return 0;
+}
+
+# elif defined __sh__
+
+/* On glibc, fexcept_t is binary-equivalent to an 'unsigned short'.
+   On all other OSes, fexcept_t is binary-equivalent to an 'unsigned int'.
+   A simple C cast does the necessary conversion.  */
+
+int
+fegetexceptflag (fexcept_t *saved_flags, int exceptions)
+{
+  /* Just like fetestexcept.  */
+  unsigned int fpscr;
+  _FPU_GETCW (fpscr);
+  *saved_flags = fpscr & FE_ALL_EXCEPT & exceptions;
+  return 0;
+}
+
+# elif defined __sparc
+
+/* On all OSes except Solaris, fexcept_t is binary-equivalent to an 'unsigned long'.
+   On Solaris, fexcept_t is an 'int'.
+   A simple C cast does the necessary conversion.  */
+
+int
+fegetexceptflag (fexcept_t *saved_flags, int exceptions)
+{
+  /* Just like fetestexcept.  */
+  unsigned long fsr;
+  _FPU_GETCW (fsr);
+#  if FE_INEXACT == 32 /* glibc compatible FE_* values */
+  *saved_flags = fsr & FE_ALL_EXCEPT & exceptions;
+#  else /* Solaris compatible FE_* values */
+  *saved_flags = (fsr >> 5) & FE_ALL_EXCEPT & exceptions;
+#  endif
+  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_FPSETSTICKY
+/* FreeBSD ≥ 3.1, NetBSD ≥ 1.1, OpenBSD, IRIX, Solaris, Minix ≥ 3.2.  */
+
+/* Get fpgetsticky, fpsetsticky.  */
+#  include <ieeefp.h>
+/* The type is called 'fp_except_t' on FreeBSD, but 'fp_except' on
+   all other systems.  */
+#  if !defined __FreeBSD__
+#   define fp_except_t fp_except
+#  endif
+
+int
+fegetexceptflag (fexcept_t *saved_flags, int exceptions)
+{
+  /* Just like fetestexcept.  */
+  fp_except_t flags = fpgetsticky ();
+  *saved_flags = flags & FE_ALL_EXCEPT & exceptions;
+  return 0;
+}
+
+# elif defined _AIX && defined __powerpc__ /* AIX */
+
+#  include <float.h>
+#  include <fpxcp.h>
+
+/* Documentation:
+   <https://www.ibm.com/docs/en/aix/7.3?topic=f-fp-clr-flag-fp-set-flag-fp-read-flag-fp-swap-flag-subroutine>  */
+
+int
+fegetexceptflag (fexcept_t *saved_flags, int exceptions)
+{
+  /* Just like fetestexcept.  */
+  fpflag_t flags = fp_read_flag ();
+  *saved_flags = fpflag_to_exceptions (flags) & FE_ALL_EXCEPT & exceptions;
+  return 0;
+}
+
+# else
+
+#  define NEED_FALLBACK 1
+
+# endif
+
+#endif
+
+#if NEED_FALLBACK
+
+/* A dummy fallback.  */
+
+int
+fegetexceptflag (fexcept_t *saved_flags, int exceptions)
+{
+  /* Just like fetestexcept.  */
+  *saved_flags = 0;
+  return 0;
+}
+
+#endif
diff --git a/lib/fenv-except-state-set.c b/lib/fenv-except-state-set.c
new file mode 100644
index 0000000000..6ba4d63a7d
--- /dev/null
+++ b/lib/fenv-except-state-set.c
@@ -0,0 +1,597 @@
+/* Functions for saving the floating-point exception status flags.
+   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>/{fgetexcptflg.c,fsetexcptflg.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)
+
+/* On most OSes, fexcept_t is binary-equivalent to an 'unsigned short'.
+   On NetBSD, OpenBSD, Solaris, Cygwin, MSVC, Android/x86_64, Minix, fexcept_t
+   is equivalent to an 'unsigned int'.
+   A simple C cast does the necessary conversion.  */
+
+int
+fesetexceptflag (fexcept_t const *saved_flags, int exceptions)
+{
+  exceptions &= FE_ALL_EXCEPT;
+
+  unsigned int desired_flags = (unsigned int) *saved_flags;
+
+#  if defined _MSC_VER
+  exceptions = exceptions_to_x86hardware (exceptions);
+  desired_flags = exceptions_to_x86hardware (desired_flags);
+
+  /* Modify the flags in the SSE unit.  */
+  unsigned int mxcsr, orig_mxcsr;
+  _FPU_GETSSECW (orig_mxcsr);
+  mxcsr = orig_mxcsr ^ ((orig_mxcsr ^ desired_flags) & exceptions);
+  if (mxcsr != orig_mxcsr)
+    _FPU_SETSSECW (mxcsr);
+
+#  else
+
+  /* The flags can be set in the 387 unit or in the SSE unit.
+     When we need to clear a flag, we need to do so in both units,
+     due to the way fetestexcept() is implemented.
+     When we need to set a flag, it is sufficient to do it in the SSE unit,
+     because that is guaranteed to not trap.  However, on i386 CPUs that have
+     only a 387 unit, set the flags in the 387, as long as this cannot trap.  */
+
+  if (CPU_HAS_SSE ())
+    {
+      /* Modify the flags in the SSE unit.  */
+      unsigned int mxcsr, orig_mxcsr;
+      _FPU_GETSSECW (orig_mxcsr);
+      mxcsr = orig_mxcsr ^ ((orig_mxcsr ^ desired_flags) & exceptions);
+      if (mxcsr != orig_mxcsr)
+        _FPU_SETSSECW (mxcsr);
+
+      /* Modify the flags in the 387 unit, but only by clearing bits, not by
+         setting bits.  */
+      x86_387_fenv_t env;
+      unsigned short orig_status_word;
+      __asm__ __volatile__ ("fnstenv %0" : "=m" (*&env));
+      orig_status_word = env.__status_word;
+      env.__status_word &= ~ (exceptions & ~desired_flags);
+      if (env.__status_word != orig_status_word)
+        __asm__ __volatile__ ("fldenv %0" : : "m" (*&env));
+    }
+#   if !(defined __x86_64__ || defined _M_X64)
+  else
+    {
+      /* Modify the flags in the 387 unit.  */
+      x86_387_fenv_t env;
+      unsigned short orig_status_word;
+      __asm__ __volatile__ ("fnstenv %0" : "=m" (*&env));
+      orig_status_word = env.__status_word;
+      env.__status_word ^= ((env.__status_word ^ desired_flags) & exceptions);
+      if (env.__status_word != orig_status_word)
+        {
+          if ((~env.__control_word) & env.__status_word & exceptions)
+            {
+              /* Setting the exception flags may trigger a trap (at the next
+                 floating-point instruction, but that does not matter).
+                 ISO C 23 § 7.6.4.5 does not allow it.  */
+              return -1;
+            }
+          __asm__ __volatile__ ("fldenv %0" : : "m" (*&env));
+        }
+    }
+#   endif
+#  endif
+  return 0;
+}
+
+# elif defined __aarch64__ /* arm64 */
+
+/* On Linux, NetBSD, and Android, fexcept_t is binary-equivalent to
+   an 'unsigned int'.
+   On macOS, fexcept_t is binary-equivalent to an 'unsigned short'.
+   On FreeBSD and OpenBSD, fexcept_t is binary-equivalent to an 'unsigned long'.
+   A simple C cast does the necessary conversion.  */
+
+int
+fesetexceptflag (fexcept_t const *saved_flags, int exceptions)
+{
+  unsigned long desired_flags = (unsigned long) *saved_flags;
+  unsigned long fpsr, orig_fpsr;
+  _FPU_GETFPSR (orig_fpsr);
+  fpsr = orig_fpsr ^ ((orig_fpsr ^ desired_flags) & FE_ALL_EXCEPT & exceptions);
+  if (fpsr != orig_fpsr)
+    _FPU_SETFPSR (fpsr);
+  /* Possibly some new exception flags have been set.  But just storing them
+     does not cause a trap to be executed (when traps are enabled).  */
+  return 0;
+}
+
+# elif defined __arm__
+
+/* On all OSes, fexcept_t is binary-equivalent to an 'unsigned int'.  */
+
+int
+fesetexceptflag (fexcept_t const *saved_flags, int exceptions)
+{
+#  ifdef __SOFTFP__
+  if (exceptions != 0)
+    return -1;
+#  else
+  unsigned int desired_flags = (unsigned int) *saved_flags;
+  unsigned int fpscr, orig_fpscr;
+  _FPU_GETCW (orig_fpscr);
+  fpscr = orig_fpscr ^ ((orig_fpscr ^ desired_flags) & FE_ALL_EXCEPT & exceptions);
+  if (fpscr != orig_fpscr)
+    _FPU_SETCW (fpscr);
+#  endif
+  return 0;
+}
+
+# elif defined __alpha
+
+/* On all OSes except NetBSD and OpenBSD, fexcept_t is binary-equivalent to
+   an 'unsigned long'.
+   On NetBSD, it is equivalent to an 'unsigned short'.
+   On OpenBSD, it is equivalent to an 'unsigned int'.
+   A simple C cast does the necessary conversion.  */
+
+int
+fesetexceptflag (fexcept_t const *saved_flags, int exceptions)
+{
+  unsigned long desired_flags = (unsigned long) *saved_flags;
+  unsigned long swcr, orig_swcr;
+  orig_swcr = __ieee_get_fp_control ();
+  swcr = orig_swcr ^ ((orig_swcr ^ desired_flags) & FE_ALL_EXCEPT & exceptions);
+  if (swcr != orig_swcr)
+    __ieee_set_fp_control (swcr);
+  /* Possibly some new exception flags have been set.  But just storing them
+     does not cause a trap to be executed (when traps are enabled).  */
+  return 0;
+}
+
+# elif defined __hppa
+
+/* On all OSes, fexcept_t is binary-equivalent to an 'unsigned int'.  */
+
+int
+fesetexceptflag (fexcept_t const *saved_flags, int exceptions)
+{
+  unsigned int desired_flags = (unsigned int) *saved_flags;
+  union { unsigned long long fpreg; unsigned int halfreg[2]; } s;
+  /* Get the current status word. */
+  __asm__ __volatile__ ("fstd %%fr0,0(%1)" : "=m" (s.fpreg) : "r" (&s.fpreg) : "%r0");
+  unsigned int old_halfreg0 = s.halfreg[0];
+  /* Modify all the relevant bits. */
+  s.halfreg[0] ^= (s.halfreg[0] ^ (desired_flags << 27)) & ((FE_ALL_EXCEPT & exceptions) << 27);
+  if (s.halfreg[0] != old_halfreg0)
+    {
+      /* Store the new status word.  */
+      __asm__ __volatile__ ("fldd 0(%0),%%fr0" : : "r" (&s.fpreg), "m" (s.fpreg) : "%r0");
+    }
+  /* Possibly some new exception flags have been set.  But just storing them
+     does not cause a trap to be executed (when traps are enabled).  */
+  return 0;
+}
+
+# elif defined __ia64__
+
+/* On all OSes except NetBSD, fexcept_t is binary-equivalent to
+   an 'unsigned long'.
+   On NetBSD, it is equivalent to an 'unsigned short'.
+   A simple C cast does the necessary conversion.  */
+
+int
+fesetexceptflag (fexcept_t const *saved_flags, int exceptions)
+{
+  unsigned long desired_flags = (unsigned long) *saved_flags;
+  unsigned long fpsr, orig_fpsr;
+  _FPU_GETCW (orig_fpsr);
+  fpsr = orig_fpsr ^ ((orig_fpsr ^ (desired_flags << 13)) & ((FE_ALL_EXCEPT & exceptions) << 13));
+  if (fpsr != orig_fpsr)
+    _FPU_SETCW (fpsr);
+  /* Possibly some new exception flags have been set.  But just storing them
+     does not cause a trap to be executed (when traps are enabled).  */
+  return 0;
+}
+
+# elif defined __m68k__
+
+/* On all OSes, fexcept_t is binary-equivalent to an 'unsigned int'.  */
+
+int
+fesetexceptflag (fexcept_t const *saved_flags, int exceptions)
+{
+  unsigned int desired_flags = (unsigned int) *saved_flags;
+  unsigned int fpsr, orig_fpsr;
+  _FPU_GETFPSR (orig_fpsr);
+  fpsr = orig_fpsr ^ ((orig_fpsr ^ desired_flags) & FE_ALL_EXCEPT & exceptions);
+  if (fpsr != orig_fpsr)
+    _FPU_SETFPSR (fpsr);
+  /* Possibly some new exception flags have been set.  But just storing them
+     does not cause a trap to be executed (when traps are enabled).  */
+  return 0;
+}
+
+# elif defined __mips__
+
+/* On all OSes except NetBSD and OpenBSD, fexcept_t is binary-equivalent to
+   an 'unsigned short'.
+   On NetBSD and OpenBSD, it is equivalent to an 'unsigned int'.
+   A simple C cast does the necessary conversion.  */
+
+int
+fesetexceptflag (fexcept_t const *saved_flags, int exceptions)
+{
+  unsigned int desired_flags = (unsigned int) *saved_flags;
+  unsigned int fcsr, orig_fcsr;
+  _FPU_GETCW (orig_fcsr);
+  fcsr = orig_fcsr ^ ((orig_fcsr ^ desired_flags) & FE_ALL_EXCEPT & exceptions);
+  if (fcsr != orig_fcsr)
+    _FPU_SETCW (fcsr);
+  return 0;
+}
+
+# elif defined __loongarch__
+
+/* On all OSes, fexcept_t is binary-equivalent to an 'unsigned int'.  */
+
+int
+fesetexceptflag (fexcept_t const *saved_flags, int exceptions)
+{
+  unsigned int desired_flags = (unsigned int) *saved_flags;
+  unsigned int fcsr, orig_fcsr;
+  _FPU_GETCW (orig_fcsr);
+  fcsr = orig_fcsr ^ ((orig_fcsr ^ desired_flags) & FE_ALL_EXCEPT & exceptions);
+  if (fcsr != orig_fcsr)
+    _FPU_SETCW (fcsr);
+  /* Possibly some new exception flags have been set.  But just storing them
+     does not cause a trap to be executed (when traps are enabled).  */
+  return 0;
+}
+
+# elif defined __powerpc__
+
+/* On all OSes, fexcept_t is binary-equivalent to an 'unsigned int'.  */
+
+int
+fesetexceptflag (fexcept_t const *saved_flags, int exceptions)
+{
+  /* The hardware does not support setting an exception flag without triggering
+     a trap, except through the "Ignore Exceptions Mode", bits FE0 and FE1 of
+     the MSR register set to zero, that can be obtained through a system call:
+       - On Linux and NetBSD: prctl (PR_SET_FPEXC, PR_FP_EXC_DISABLED);
+       - On AIX: fp_trap (FP_TRAP_OFF);
+     But that is not what we need here, as it would have a persistent effect on
+     the thread.  */
+  exceptions &= FE_ALL_EXCEPT;
+
+  unsigned int desired_flags = (unsigned int) *saved_flags;
+  desired_flags &= exceptions;
+
+  union { unsigned long long u; double f; } memenv, orig_memenv;
+  _FPU_GETCW_AS_DOUBLE (memenv.f);
+  orig_memenv = memenv;
+
+  /* Instead of clearing FE_INVALID (= bit 29), we need to clear the
+     individual bits.  */
+  memenv.u &= ~ (exceptions & FE_INVALID
+                 ? (exceptions & ~FE_INVALID) | 0x01F80700U
+                 : exceptions);
+  /* Instead of setting FE_INVALID (= bit 29), we need to set one of the
+     individual bits: bit 10 or, if that does not work, bit 24.  */
+  memenv.u |= (desired_flags & FE_INVALID
+               ? (desired_flags & ~FE_INVALID) | (1U << 10)
+               : desired_flags);
+
+  if (!(memenv.u == orig_memenv.u))
+    {
+      if (memenv.u & (exceptions >> 22))
+        {
+          /* Setting the exception flags may trigger a trap.
+             ISO C 23 § 7.6.4.5 does not allow it.  */
+          return -1;
+        }
+      _FPU_SETCW_AS_DOUBLE (memenv.f);
+      if (desired_flags & FE_INVALID)
+        {
+          /* Did it work?  */
+          _FPU_GETCW_AS_DOUBLE (memenv.f);
+          if ((memenv.u & FE_INVALID) == 0)
+            {
+              memenv.u |= (1U << 24);
+              _FPU_SETCW_AS_DOUBLE (memenv.f);
+            }
+        }
+    }
+
+  return 0;
+}
+
+# elif defined __riscv
+
+/* On all OSes except FreeBSD, fexcept_t is binary-equivalent to
+   an 'unsigned int'.
+   On FreeBSD, it is equivalent to an 'unsigned long'.
+   A simple C cast does the necessary conversion.  */
+
+int
+fesetexceptflag (fexcept_t const *saved_flags, int exceptions)
+{
+#  if 1 /* both should be equivalent */
+  exceptions &= FE_ALL_EXCEPT;
+
+  unsigned int desired_flags = (unsigned int) *saved_flags;
+  desired_flags &= exceptions;
+
+  __asm__ __volatile__ ("csrc fflags, %0" : : "r" (exceptions));
+  __asm__ __volatile__ ("csrs fflags, %0" : : "r" (desired_flags));
+#  else
+  unsigned int desired_flags = (unsigned int) *saved_flags;
+  unsigned int fcsr, orig_fcsr;
+  __asm__ __volatile__ ("frflags %0" : "=r" (orig_fcsr)); /* same as "csrr %0, fflags" */
+  fcsr = orig_fcsr ^ ((orig_fcsr ^ desired_flags) & FE_ALL_EXCEPT & exceptions);
+  if (fcsr != orig_fcsr)
+    __asm__ __volatile__ ("fsflags %0" : : "r" (fcsr)); /* same as "csrw fflags, %0" */
+#  endif
+
+  return 0;
+}
+
+# elif defined __s390__ || defined __s390x__
+
+/* On all OSes, fexcept_t is binary-equivalent to an 'unsigned int'.  */
+
+int
+fesetexceptflag (fexcept_t const *saved_flags, int exceptions)
+{
+  unsigned int desired_flags = (unsigned int) *saved_flags;
+  unsigned int fpc, orig_fpc;
+  _FPU_GETCW (orig_fpc);
+#  if FE_INEXACT == 8 /* glibc compatible FE_* values */
+  fpc = orig_fpc ^ ((orig_fpc ^ (desired_flags << 16)) & ((FE_ALL_EXCEPT & exceptions) << 16));
+  if ((fpc & 0x00000300) == 0)
+    /* Clear the corresponding bits of the "data exception code".  */
+    fpc &= ~((FE_ALL_EXCEPT & exceptions) << 8);
+#  else /* musl libc compatible FE_* values */
+  fpc = orig_fpc ^ ((orig_fpc ^ desired_flags) & FE_ALL_EXCEPT & exceptions);
+  if ((fpc & 0x00000300) == 0)
+    /* Clear the corresponding bits of the "data exception code".  */
+    fpc &= ~((FE_ALL_EXCEPT & exceptions) >> 8);
+#  endif
+  if (fpc != orig_fpc)
+    _FPU_SETCW (fpc);
+  /* Possibly some new exception flags have been set.  But just storing them
+     does not cause a trap to be executed (when traps are enabled).  */
+  return 0;
+}
+
+# elif defined __sh__
+
+/* On glibc, fexcept_t is binary-equivalent to an 'unsigned short'.
+   On all other OSes, fexcept_t is binary-equivalent to an 'unsigned int'.
+   A simple C cast does the necessary conversion.  */
+
+int
+fesetexceptflag (fexcept_t const *saved_flags, int exceptions)
+{
+  unsigned int desired_flags = (unsigned int) *saved_flags;
+  unsigned int fpscr, orig_fpscr;
+  _FPU_GETCW (orig_fpscr);
+  fpscr = orig_fpscr ^ ((orig_fpscr ^ desired_flags) & FE_ALL_EXCEPT & exceptions);
+  if (fpscr != orig_fpscr)
+    _FPU_SETCW (fpscr);
+  /* Possibly some new exception flags have been set.  But just storing them
+     does not cause a trap to be executed (when traps are enabled).  */
+  return 0;
+}
+
+# elif defined __sparc
+
+/* On all OSes except Solaris, fexcept_t is binary-equivalent to an 'unsigned long'.
+   On Solaris, fexcept_t is an 'int'.
+   A simple C cast does the necessary conversion.  */
+
+int
+fesetexceptflag (fexcept_t const *saved_flags, int exceptions)
+{
+  unsigned long desired_flags = (unsigned long) *saved_flags;
+  unsigned long fsr, orig_fsr;
+  _FPU_GETCW (orig_fsr);
+#  if FE_INEXACT == 32 /* glibc compatible FE_* values */
+  fsr = orig_fsr ^ ((orig_fsr ^ desired_flags) & FE_ALL_EXCEPT & exceptions);
+#  else /* Solaris compatible FE_* values */
+  fsr = orig_fsr ^ ((orig_fsr ^ (desired_flags << 5)) & ((FE_ALL_EXCEPT & exceptions) << 5));
+#  endif
+  if (fsr != orig_fsr)
+    _FPU_SETCW (fsr);
+  /* Possibly some new exception flags have been set.  But just storing them
+     does not cause a trap to be executed (when traps are enabled).  */
+  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 defined __sun && ((defined __x86_64__ || defined _M_X64) || (defined __i386 || defined _M_IX86)) && defined __SUNPRO_C
+/* Solaris/i386, Solaris/x86_64.  */
+
+/* On these platforms, fpsetsticky cannot be used here, because it may generate
+   traps (since fpsetsticky calls _putsw, which modifies the control word of the
+   387 unit).  Instead, we need to modify only the flags in the SSE unit.  */
+
+/* Accessors for the mxcsr register.  Fortunately, the Solaris cc supports a
+   poor form of 'asm'.  */
+
+static void
+getssecw (unsigned int *mxcsr_p)
+{
+#  if defined __x86_64__ || defined _M_X64
+  asm ("stmxcsr (%rdi)");
+#  else
+  /* The compiler generates a stack frame.  Therefore the first argument is in
+     8(%ebp), not in 4(%esp).  */
+  asm ("movl 8(%ebp),%eax");
+  asm ("stmxcsr (%eax)");
+#  endif
+}
+
+static void
+setssecw (unsigned int const *mxcsr_p)
+{
+#  if defined __x86_64__ || defined _M_X64
+  asm ("ldmxcsr (%rdi)");
+#  else
+  /* The compiler generates a stack frame.  Therefore the first argument is in
+     8(%ebp), not in 4(%esp).  */
+  asm ("movl 8(%ebp),%eax");
+  asm ("ldmxcsr (%eax)");
+#  endif
+}
+
+int
+fesetexceptflag (fexcept_t const *saved_flags, int exceptions)
+{
+  exceptions &= FE_ALL_EXCEPT;
+  unsigned int desired_flags = (unsigned int) *saved_flags;
+
+  /* Modify the flags in the SSE unit.  */
+  unsigned int mxcsr, orig_mxcsr;
+  getssecw (&orig_mxcsr);
+  mxcsr = orig_mxcsr ^ ((orig_mxcsr ^ desired_flags) & exceptions);
+  if (mxcsr != orig_mxcsr)
+    setssecw (&mxcsr);
+
+  return 0;
+}
+
+# elif HAVE_FPSETSTICKY
+/* FreeBSD ≥ 3.1, NetBSD ≥ 1.1, OpenBSD, IRIX, Solaris, Minix ≥ 3.2.  */
+
+/* Get fpgetsticky, fpsetsticky.  */
+#  include <ieeefp.h>
+/* The type is called 'fp_except_t' on FreeBSD, but 'fp_except' on
+   all other systems.  */
+#  if !defined __FreeBSD__
+#   define fp_except_t fp_except
+#  endif
+
+int
+fesetexceptflag (fexcept_t const *saved_flags, int exceptions)
+{
+  unsigned long desired_flags = (unsigned long) *saved_flags;
+  fp_except_t flags, orig_flags;
+  orig_flags = fpgetsticky ();
+  flags = orig_flags ^ ((orig_fsr ^ desired_flags) & FE_ALL_EXCEPT & exceptions);
+  if (flags != orig_flags)
+    fpsetsticky (flags);
+  /* Possibly some new exception flags have been set.  But just storing them
+     does not cause a trap to be executed (when traps are enabled).  */
+  return 0;
+}
+
+# elif defined _AIX && defined __powerpc__ /* AIX */
+
+#  include <float.h>
+#  include <fpxcp.h>
+
+#  include <fptrap.h>
+
+/* Documentation:
+   <https://www.ibm.com/docs/en/aix/7.3?topic=f-fp-clr-flag-fp-set-flag-fp-read-flag-fp-swap-flag-subroutine>  */
+
+int
+fesetexceptflag (fexcept_t const *saved_flags, int exceptions)
+{
+  exceptions &= FE_ALL_EXCEPT;
+
+  unsigned int desired_flags = (unsigned int) *saved_flags;
+  int exceptions_to_clear = exceptions & ~desired_flags;
+  int exceptions_to_set = exceptions & desired_flags;
+
+  fpflag_t orig_flags = fp_read_flag ();
+  /* In addition to clearing FE_INVALID (= bit 29), we also need to clear the
+     individual bits.  */
+  fpflag_t f_to_clear =
+    exceptions_to_fpflag (exceptions_to_clear)
+    | (exceptions_to_clear & FE_INVALID ? 0x01F80700U : 0);
+  /* Instead of setting FE_INVALID (= bit 29), we need to set one of the
+     individual bits: bit 10 or, if that does not work, bit 24.  */
+  fpflag_t f_to_set =
+    (exceptions_to_set & FE_INVALID
+     ? exceptions_to_fpflag (exceptions_to_set & ~FE_INVALID) | (1U << 10)
+     : exceptions_to_fpflag (exceptions_to_set));
+  if (f_to_clear != 0)
+    fp_clr_flag (f_to_clear);
+  if (f_to_set != 0)
+    {
+      if ((fegetexcept_impl () & exceptions) != 0)
+        {
+          /* Setting the exception flags may trigger a trap.
+             ISO C 23 § 7.6.4.5 does not allow it.  */
+          return -1;
+        }
+      fp_set_flag (f_to_set);
+      if (exceptions & FE_INVALID)
+        {
+          /* Did it work?  */
+          if ((fp_read_flag () & FP_INVALID) == 0)
+            fp_set_flag (1U << 24);
+        }
+    }
+
+  return 0;
+}
+
+# else
+
+#  define NEED_FALLBACK 1
+
+# endif
+
+#endif
+
+#if NEED_FALLBACK
+
+/* A dummy fallback.  */
+
+int
+fesetexceptflag (fexcept_t const *saved_flags, int exceptions)
+{
+  if (exceptions != 0)
+    return -1;
+  return 0;
+}
+
+#endif
diff --git a/lib/fenv.in.h b/lib/fenv.in.h
index 0c119c5936..ad61e2c4c0 100644
--- a/lib/fenv.in.h
+++ b/lib/fenv.in.h
@@ -544,7 +544,8 @@ _GL_WARN_ON_USE (fesetexcept, "fesetexcept is unportable - "
 
 /* ISO C 99 § 7.6.2 Floating-point exceptions
    ISO C 23 § 7.6.4 Floating-point exceptions
-   API with fexcept_t  */
+   API with fexcept_t.
+   The return type changed from 'void' to 'int' in ISO C 11.  */
 
 #if !@HAVE_FENV_H@
 
@@ -610,6 +611,52 @@ typedef unsigned long fexcept_t;
 
 #endif
 
+#if @GNULIB_FEGETEXCEPTFLAG@
+/* Stores the current floating-point exception status flags denoted
+   by EXCEPTIONS in *SAVED_FLAGS.  */
+# if @REPLACE_FEGETEXCEPTFLAG@
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   undef fegetexceptflag
+#   define fegetexceptflag rpl_fegetexceptflag
+#  endif
+_GL_FUNCDECL_RPL (fegetexceptflag, int,
+                  (fexcept_t *saved_flags, int exceptions));
+_GL_CXXALIAS_RPL (fegetexceptflag, int,
+                  (fexcept_t *saved_flags, int exceptions));
+# else
+#  if !@HAVE_FEGETEXCEPTFLAG@
+_GL_FUNCDECL_SYS (fegetexceptflag, int,
+                  (fexcept_t *saved_flags, int exceptions));
+#  endif
+_GL_CXXALIAS_SYS (fegetexceptflag, int,
+                  (fexcept_t *saved_flags, int exceptions));
+# endif
+_GL_CXXALIASWARN (fegetexceptflag);
+#endif
+
+#if @GNULIB_FESETEXCEPTFLAG@
+/* Copies the flags denoted by EXCEPTIONS from *SAVED_FLAGS to the
+   floating-point exception status flags.  */
+# if @REPLACE_FESETEXCEPTFLAG@
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   undef fesetexceptflag
+#   define fesetexceptflag rpl_fesetexceptflag
+#  endif
+_GL_FUNCDECL_RPL (fesetexceptflag, int,
+                  (fexcept_t const *saved_flags, int exceptions));
+_GL_CXXALIAS_RPL (fesetexceptflag, int,
+                  (fexcept_t const *saved_flags, int exceptions));
+# else
+#  if !@HAVE_FESETEXCEPTFLAG@
+_GL_FUNCDECL_SYS (fesetexceptflag, int,
+                  (fexcept_t const *saved_flags, int exceptions));
+#  endif
+_GL_CXXALIAS_SYS (fesetexceptflag, int,
+                  (fexcept_t const *saved_flags, int exceptions));
+# endif
+_GL_CXXALIASWARN (fesetexceptflag);
+#endif
+
 
 #endif /* _@GUARD_PREFIX@_FENV_H */
 #endif /* _@GUARD_PREFIX@_FENV_H */
diff --git a/m4/fenv-exceptions-state.m4 b/m4/fenv-exceptions-state.m4
new file mode 100644
index 0000000000..bd443be721
--- /dev/null
+++ b/m4/fenv-exceptions-state.m4
@@ -0,0 +1,221 @@
+# fenv-exceptions-state.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_EXCEPTIONS_STATE],
+[
+  AC_REQUIRE([gl_FENV_H_DEFAULTS])
+  AC_REQUIRE([AC_CANONICAL_HOST])
+
+  gl_MATHFUNC([fesetexceptflag], [int], [(fexcept_t const *, int)],
+    [#include <fenv.h>
+     fexcept_t fx_ret;
+    ])
+  if test $gl_cv_func_fesetexceptflag_no_libm = yes \
+     || test $gl_cv_func_fesetexceptflag_in_libm = yes; then
+    dnl It needs linking with -lm on
+    dnl glibc, FreeBSD, NetBSD, OpenBSD, AIX, HP-UX, IRIX, Solaris, Android.
+    if test $gl_cv_func_fesetexceptflag_no_libm = yes; then
+      FENV_EXCEPTIONS_STATE_LIBM=
+    else
+      FENV_EXCEPTIONS_STATE_LIBM=-lm
+    fi
+    dnl On glibc 2.19/s390,s390x, fegetexceptflag is not consistent with the
+    dnl generic implementation of fetestexceptflag, leading to a failure of
+    dnl test-fenv-except-state-3. Fixed through
+    dnl <https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=5d96fe8c0dc3450bafe6c2aae2dabc76819df3e0>.
+    case "$host" in
+      s390*-*-linux*)
+        AC_CACHE_CHECK([whether fegetexceptflag works],
+          [gl_cv_func_fegetexceptflag_works],
+          [AC_COMPILE_IFELSE(
+             [AC_LANG_PROGRAM([[
+                #include <string.h> /* for __GNU_LIBRARY__ */
+                #ifdef __GNU_LIBRARY__
+                 #include <features.h>
+                 #if __GLIBC__ == 2 && __GLIBC_MINOR__ <= 22
+                  Unlucky user
+                 #endif
+                #endif
+                ]],
+                [])],
+             [gl_cv_func_fegetexceptflag_works="guessing yes"],
+             [gl_cv_func_fegetexceptflag_works="guessing no"])
+          ])
+        case "$gl_cv_func_fegetexceptflag_works" in
+          *yes) ;;
+          *)
+            REPLACE_FEGETEXCEPTFLAG=1
+            REPLACE_FESETEXCEPTFLAG=1
+            ;;
+        esac
+        ;;
+    esac
+    if test $REPLACE_FESETEXCEPTFLAG = 0; then
+      dnl Persuade glibc <fenv.h> to declare feenableexcept().
+      AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
+      gl_MATHFUNC([feenableexcept], [int], [(int)], [#include <fenv.h>])
+      dnl On glibc 2.37 for PowerPC, i386, x86_64, fesetexceptflag may raise
+      dnl traps. Likewise on Mac OS X 10.5.8 on i386, x86_64 and on mingw.
+      AC_CACHE_CHECK([whether fesetexceptflag is guaranteed non-trapping],
+        [gl_cv_func_fesetexceptflag_works1],
+        [if test $gl_cv_func_feenableexcept_no_libm = yes \
+            || test $gl_cv_func_feenableexcept_in_libm = yes; then
+           dnl A platform that has feenableexcept.
+           save_LIBS="$LIBS"
+           if test $gl_cv_func_feenableexcept_no_libm != yes; then
+             LIBS="$LIBS -lm"
+           fi
+           AC_RUN_IFELSE(
+             [AC_LANG_PROGRAM([[
+                #include <fenv.h>
+                static volatile double a, b;
+                static volatile long double al, bl;
+                ]],
+                [[fexcept_t saved_flags;
+                  if (feraiseexcept (FE_INVALID) == 0
+                      && fegetexceptflag (&saved_flags, FE_INVALID) == 0
+                      && feclearexcept (FE_INVALID) == 0
+                      && feenableexcept (FE_INVALID) == 0
+                      && fesetexceptflag (&saved_flags, FE_INVALID) == 0)
+                    {
+                      a = 1.0; b = a + a;
+                      al = 1.0L; bl = al + al;
+                    }
+                  return 0;
+                ]])
+             ],
+             [gl_cv_func_fesetexceptflag_works1=yes],
+             [gl_cv_func_fesetexceptflag_works1=no],
+             [case "$host_os" in
+                # Guess yes or no on glibc systems, depending on CPU.
+                *-gnu*)
+                  case "$host_cpu" in
+changequote(,)dnl
+                    powerpc* | i[34567]86 | x86_64)
+changequote([,])dnl
+                      gl_cv_func_fesetexceptflag_works1="guessing no" ;;
+                    *)
+                      gl_cv_func_fesetexceptflag_works1="guessing yes" ;;
+                  esac
+                  ;;
+                # If we don't know, obey --enable-cross-guesses.
+                *) gl_cv_func_fesetexceptflag_works1="$gl_cross_guess_normal" ;;
+              esac
+             ])
+           LIBS="$save_LIBS"
+         else
+           case "$host_os" in
+             # Guess no on musl libc.
+             # In musl/src/fenv/fesetexceptflag.c, fesetexceptflag() explicitly
+             # invokes feraiseexcept(). It's mentioned in
+             # <https://wiki.musl-libc.org/functional-differences-from-glibc.html>.
+             *-musl* | midipix*)
+               gl_cv_func_fesetexceptflag_works1="guessing no"
+               ;;
+             # Guess no on macOS.
+             darwin*)
+               gl_cv_func_fesetexceptflag_works1="guessing no"
+               ;;
+             # Guess no on mingw.
+             mingw* | windows*)
+               AC_EGREP_CPP([Problem], [
+#ifdef __MINGW32__
+ Problem
+#endif
+                 ],
+                 [gl_cv_func_fesetexceptflag_works1="guessing no"],
+                 [gl_cv_func_fesetexceptflag_works1="guessing yes"])
+               ;;
+             *) gl_cv_func_fesetexceptflag_works1="guessing yes" ;;
+           esac
+         fi
+        ])
+      dnl On glibc 2.37 for alpha, fesetexceptflag clears too many flag bits.
+      AC_CACHE_CHECK([whether fesetexceptflag obeys its arguments],
+        [gl_cv_func_fesetexceptflag_works2],
+        [save_LIBS="$LIBS"
+         LIBS="$LIBS $FENV_EXCEPTIONS_STATE_LIBM"
+         AC_RUN_IFELSE(
+           [AC_LANG_PROGRAM([[
+              #include <fenv.h>
+              ]],
+              [[fexcept_t f1, f2;
+                if (feraiseexcept (FE_DIVBYZERO | FE_OVERFLOW | FE_INEXACT) == 0
+                    && fegetexceptflag (&f1, FE_DIVBYZERO | FE_OVERFLOW | FE_INEXACT) == 0
+                    && feclearexcept (FE_OVERFLOW | FE_INEXACT) == 0
+                    && fegetexceptflag (&f2, FE_OVERFLOW) == 0
+                    && fesetexceptflag (&f1, FE_DIVBYZERO | FE_OVERFLOW) == 0
+                    && fesetexceptflag (&f2, FE_OVERFLOW) == 0
+                    && fetestexcept (FE_DIVBYZERO) != FE_DIVBYZERO)
+                  return 1;
+                return 0;
+              ]])
+           ],
+           [gl_cv_func_fesetexceptflag_works2=yes],
+           [gl_cv_func_fesetexceptflag_works2=no],
+           [case "$host_os" in
+              # Guess yes or no on glibc systems, depending on CPU.
+              *-gnu*)
+                case "$host_cpu" in
+                  alpha*)
+                    gl_cv_func_fesetexceptflag_works2="guessing no" ;;
+                  *)
+                    gl_cv_func_fesetexceptflag_works2="guessing yes" ;;
+                esac
+                ;;
+              # If we don't know, obey --enable-cross-guesses.
+              *) gl_cv_func_fesetexceptflag_works2="$gl_cross_guess_normal" ;;
+            esac
+           ])
+         LIBS="$save_LIBS"
+        ])
+      case "$gl_cv_func_fesetexceptflag_works1" in
+        *yes) ;;
+        *) REPLACE_FESETEXCEPTFLAG=1 ;;
+      esac
+      case "$gl_cv_func_fesetexceptflag_works2" in
+        *yes) ;;
+        *) REPLACE_FESETEXCEPTFLAG=1 ;;
+      esac
+      dnl Additionally, on AIX, the unit test test-fenv-except-state-1 fails
+      dnl if we don't override fesetexceptflag.
+      case "$host" in
+        powerpc*-*-aix*) REPLACE_FESETEXCEPTFLAG=1 ;;
+      esac
+      dnl Additionally, on MSVC, we want the bits in the saved state to be
+      dnl identified by the FE_* macros, so that the fetestexceptflag function
+      dnl can be implemented like on other platforms. This requires conversions
+      dnl (exceptions_to_x86hardware, x86hardware_to_exceptions) in both
+      dnl of the fegetexceptflag, fesetexceptflag functions.
+      case "$host_os" in
+        mingw* | windows*)
+          AC_EGREP_CPP([Problem], [
+#ifndef __MINGW32__
+ Problem
+#endif
+            ],
+            [REPLACE_FEGETEXCEPTFLAG=1; REPLACE_FESETEXCEPTFLAG=1],
+            [])
+          ;;
+      esac
+    fi
+  else
+    HAVE_FEGETEXCEPTFLAG=0
+    HAVE_FESETEXCEPTFLAG=0
+    FENV_EXCEPTIONS_STATE_LIBM=
+  fi
+  if { test $HAVE_FEGETEXCEPTFLAG = 0 || test $REPLACE_FEGETEXCEPTFLAG = 1; } \
+     || { test $HAVE_FESETEXCEPTFLAG = 0 || test $REPLACE_FESETEXCEPTFLAG = 1; }; then
+    gl_PREREQ_FENV_EXCEPTIONS
+    dnl Possibly need -lm for fpgetsticky(), fpsetsticky().
+    if test $gl_cv_func_fpsetsticky_no_libm = no \
+       && test $gl_cv_func_fpsetsticky_in_libm = yes \
+       && test -z "$FENV_EXCEPTIONS_STATE_LIBM"; then
+      FENV_EXCEPTIONS_STATE_LIBM=-lm
+    fi
+  fi
+  AC_SUBST([FENV_EXCEPTIONS_STATE_LIBM])
+])
diff --git a/m4/mathfunc.m4 b/m4/mathfunc.m4
index f220cea8ac..bf83bd2b8a 100644
--- a/m4/mathfunc.m4
+++ b/m4/mathfunc.m4
@@ -1,4 +1,4 @@
-# mathfunc.m4 serial 15
+# mathfunc.m4 serial 16
 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,
@@ -41,7 +41,9 @@ AC_DEFUN([gl_MATHFUNC]
                                              [m4_bpatsubst(
                                                 [m4_bpatsubst(
                                                    [m4_bpatsubst(
-                                                      [$3],
+                                                      [m4_bpatsubst(
+                                                         [$3],
+                                                         [fexcept_t\( const\)? \*], [&fx_ret])],
                                                       [int\( const\)? \*],
                                                       [&i_ret])],
                                                    [float\( const\)? \*], [&f_ret])],
diff --git a/modules/fenv-exceptions-state-c99 b/modules/fenv-exceptions-state-c99
new file mode 100644
index 0000000000..dcf7c42c10
--- /dev/null
+++ b/modules/fenv-exceptions-state-c99
@@ -0,0 +1,43 @@
+Description:
+Functions for saving the floating-point exception status flags:
+fegetexceptflag, fesetexceptflag.
+
+Files:
+lib/fenv-except-state-get.c
+lib/fenv-except-state-set.c
+lib/fenv-private.h
+m4/fenv-exceptions-state.m4
+m4/fenv-exceptions.m4
+m4/mathfunc.m4
+
+Depends-on:
+fenv
+
+configure.ac:
+gl_FENV_EXCEPTIONS_STATE
+gl_CONDITIONAL([GL_COND_OBJ_FENV_EXCEPTIONS_STATE_GET],
+               [test $HAVE_FEGETEXCEPTFLAG = 0 || test $REPLACE_FEGETEXCEPTFLAG = 1])
+gl_CONDITIONAL([GL_COND_OBJ_FENV_EXCEPTIONS_STATE_SET],
+               [test $HAVE_FESETEXCEPTFLAG = 0 || test $REPLACE_FESETEXCEPTFLAG = 1])
+gl_FENV_MODULE_INDICATOR([fegetexceptflag])
+gl_FENV_MODULE_INDICATOR([fesetexceptflag])
+
+Makefile.am:
+if GL_COND_OBJ_FENV_EXCEPTIONS_STATE_GET
+lib_SOURCES += fenv-except-state-get.c
+endif
+if GL_COND_OBJ_FENV_EXCEPTIONS_STATE_SET
+lib_SOURCES += fenv-except-state-set.c
+endif
+
+Include:
+#include <fenv.h>
+
+Link:
+$(FENV_EXCEPTIONS_STATE_LIBM)
+
+License:
+LGPLv2+
+
+Maintainer:
+all
-- 
2.34.1

>From 68ec44335595af475b06738d5db3fe777fe804b8 Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Mon, 30 Oct 2023 16:44:26 +0100
Subject: [PATCH 2/2] fenv-exceptions-state-c99: Add tests.

* tests/test-fenv-except-state-1.c: New file.
* tests/test-fenv-except-state-2.c: New file.
* modules/fenv-exceptions-state-c99-tests: New file.
---
 ChangeLog                               |   5 +
 modules/fenv-exceptions-state-c99-tests |  22 +++++
 tests/test-fenv-except-state-1.c        | 123 ++++++++++++++++++++++++
 tests/test-fenv-except-state-2.c        |  93 ++++++++++++++++++
 4 files changed, 243 insertions(+)
 create mode 100644 modules/fenv-exceptions-state-c99-tests
 create mode 100644 tests/test-fenv-except-state-1.c
 create mode 100644 tests/test-fenv-except-state-2.c

diff --git a/ChangeLog b/ChangeLog
index 291e89ec60..e1f5066d4f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,10 @@
 2023-10-30  Bruno Haible  <br...@clisp.org>
 
+	fenv-exceptions-state-c99: Add tests.
+	* tests/test-fenv-except-state-1.c: New file.
+	* tests/test-fenv-except-state-2.c: New file.
+	* modules/fenv-exceptions-state-c99-tests: New file.
+
 	fenv-exceptions-state-c99: New module.
 	* lib/fenv.in.h (fegetexceptflag, fesetexceptflag): New declarations.
 	* lib/fenv-except-state-get.c: New file, baed on glibc.
diff --git a/modules/fenv-exceptions-state-c99-tests b/modules/fenv-exceptions-state-c99-tests
new file mode 100644
index 0000000000..7fd4e24aa1
--- /dev/null
+++ b/modules/fenv-exceptions-state-c99-tests
@@ -0,0 +1,22 @@
+Files:
+tests/test-fenv-except-state-1.c
+tests/test-fenv-except-state-2.c
+tests/macros.h
+m4/musl.m4
+
+Depends-on:
+fenv-exceptions-tracking-c99
+fpe-trapping
+
+configure.ac:
+gl_MUSL_LIBC
+
+Makefile.am:
+TESTS += \
+  test-fenv-except-state-1 \
+  test-fenv-except-state-2
+check_PROGRAMS += \
+  test-fenv-except-state-1 \
+  test-fenv-except-state-2
+test_fenv_except_state_1_LDADD = $(LDADD) @FENV_EXCEPTIONS_STATE_LIBM@ $(FENV_EXCEPTIONS_TRACKING_LIBM)
+test_fenv_except_state_2_LDADD = $(LDADD) @FENV_EXCEPTIONS_STATE_LIBM@ $(FENV_EXCEPTIONS_TRACKING_LIBM) @FPE_TRAPPING_LIBM@
diff --git a/tests/test-fenv-except-state-1.c b/tests/test-fenv-except-state-1.c
new file mode 100644
index 0000000000..3b01418d3a
--- /dev/null
+++ b/tests/test-fenv-except-state-1.c
@@ -0,0 +1,123 @@
+/* Test of saving the floating-point exception status flags.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program 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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <br...@clisp.org>, 2023.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include <fenv.h>
+
+#include "macros.h"
+
+int
+main ()
+{
+  fexcept_t saved_flags_1;
+  fexcept_t saved_flags_2;
+
+  /* Test setting all exception flags.  */
+  if (feraiseexcept (FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW | FE_INEXACT) != 0)
+    {
+      fputs ("Skipping test: floating-point exceptions are not supported on this machine.\n", stderr);
+      return 77;
+    }
+
+  /* Fill saved_flags_1.  */
+  ASSERT (fegetexceptflag (&saved_flags_1,
+                           FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW | FE_INEXACT)
+          == 0);
+
+  /* Clear some of the exception flags.  */
+  ASSERT (feclearexcept (FE_OVERFLOW | FE_UNDERFLOW | FE_INEXACT) == 0);
+  /* Here, the set exception flags are FE_INVALID | FE_DIVBYZERO.  */
+  ASSERT (fetestexcept (FE_INVALID) == FE_INVALID);
+  ASSERT (fetestexcept (FE_DIVBYZERO) == FE_DIVBYZERO);
+  ASSERT (fetestexcept (FE_OVERFLOW) == 0);
+  ASSERT (fetestexcept (FE_UNDERFLOW) == 0);
+  ASSERT (fetestexcept (FE_INEXACT) == 0);
+
+  /* Fill saved_flags_2.  */
+  ASSERT (fegetexceptflag (&saved_flags_2, FE_INVALID | FE_OVERFLOW) == 0);
+
+  /* Restore some of the exception flags.  */
+  ASSERT (fesetexceptflag (&saved_flags_1,
+                           FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW) == 0);
+  /* Here, the set exception flags are FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW.  */
+  ASSERT (fetestexcept (FE_INVALID) == FE_INVALID);
+  ASSERT (fetestexcept (FE_DIVBYZERO) == FE_DIVBYZERO);
+  ASSERT (fetestexcept (FE_OVERFLOW) == FE_OVERFLOW);
+  ASSERT (fetestexcept (FE_UNDERFLOW) == FE_UNDERFLOW);
+  ASSERT (fetestexcept (FE_INEXACT) == 0);
+
+  /* Clear some more exception flags.  */
+  ASSERT (feclearexcept (FE_INVALID) == 0);
+  /* Here, the set exception flags are FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW.  */
+  ASSERT (fetestexcept (FE_INVALID) == 0);
+  ASSERT (fetestexcept (FE_DIVBYZERO) == FE_DIVBYZERO);
+  ASSERT (fetestexcept (FE_OVERFLOW) == FE_OVERFLOW);
+  ASSERT (fetestexcept (FE_UNDERFLOW) == FE_UNDERFLOW);
+  ASSERT (fetestexcept (FE_INEXACT) == 0);
+
+  /* Restore some more exception flags.  */
+  ASSERT (fesetexceptflag (&saved_flags_2, FE_OVERFLOW) == 0);
+  /* Here, the set exception flags are FE_DIVBYZERO | FE_UNDERFLOW.  */
+  ASSERT (fetestexcept (FE_INVALID) == 0);
+  ASSERT (fetestexcept (FE_DIVBYZERO) == FE_DIVBYZERO);
+  ASSERT (fetestexcept (FE_OVERFLOW) == 0);
+  ASSERT (fetestexcept (FE_UNDERFLOW) == FE_UNDERFLOW);
+  ASSERT (fetestexcept (FE_INEXACT) == 0);
+
+  /* Restore some more exception flags.  */
+  ASSERT (fesetexceptflag (&saved_flags_2, FE_INVALID) == 0);
+  /* Here, the set exception flags are FE_INVALID | FE_DIVBYZERO | FE_UNDERFLOW.  */
+  ASSERT (fetestexcept (FE_INVALID) == FE_INVALID);
+  ASSERT (fetestexcept (FE_DIVBYZERO) == FE_DIVBYZERO);
+  ASSERT (fetestexcept (FE_OVERFLOW) == 0);
+  ASSERT (fetestexcept (FE_UNDERFLOW) == FE_UNDERFLOW);
+  ASSERT (fetestexcept (FE_INEXACT) == 0);
+
+  /* ======================================================================== */
+  /* Check that fesetexceptflag clears exception flags in both the 387 unit
+     and the SSE unit, on i386 and x86_64 CPUs.  */
+
+  fexcept_t saved_flags_3;
+
+  ASSERT (feclearexcept (FE_INVALID) == 0);
+
+  ASSERT (fegetexceptflag (&saved_flags_3, FE_INVALID) == 0);
+
+  /* Set the FE_INVALID flag in the SSE unit.  */
+  {
+    static volatile double a, b;
+    _GL_UNUSED volatile double c;
+    a = 0.0; b = 0.0; c = a / b;
+  }
+  /* Set the FE_INVALID flag in the 387 unit.  */
+  {
+    static volatile long double al, bl;
+    _GL_UNUSED volatile long double cl;
+    al = 0.0L; bl = 0.0L; cl = al / bl;
+  }
+
+  /* Use fesetexceptflag to clear the FE_INVALID flag.  */
+  ASSERT (fesetexceptflag (&saved_flags_3, FE_INVALID) == 0);
+
+  /* Check that it's clear in both units.  */
+  ASSERT (fetestexcept (FE_INVALID) == 0);
+
+  return 0;
+}
diff --git a/tests/test-fenv-except-state-2.c b/tests/test-fenv-except-state-2.c
new file mode 100644
index 0000000000..b87fbb5a87
--- /dev/null
+++ b/tests/test-fenv-except-state-2.c
@@ -0,0 +1,93 @@
+/* Test of saving the floating-point exception status flags.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program 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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <br...@clisp.org>, 2023.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include <fenv.h>
+
+#include <stdio.h>
+
+#include "fpe-trapping.h"
+#include "macros.h"
+
+/* musl libc does not support floating-point exception trapping, even where
+   the hardware supports it.  See
+   <https://wiki.musl-libc.org/functional-differences-from-glibc.html>  */
+#if HAVE_FPE_TRAPPING && (!MUSL_LIBC || GNULIB_FEENABLEEXCEPT)
+
+/* Check that fesetexceptflag() does not trigger a trap.  */
+
+static volatile double a, b;
+static volatile long double al, bl;
+
+int
+main ()
+{
+  fexcept_t saved_flags_1;
+
+  /* Test setting all exception flags.  */
+  if (feraiseexcept (FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW | FE_INEXACT) != 0)
+    {
+      fputs ("Skipping test: floating-point exceptions are not supported on this machine.\n", stderr);
+      return 77;
+    }
+
+  /* Fill saved_flags_1.  */
+  ASSERT (fegetexceptflag (&saved_flags_1,
+                           FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW | FE_INEXACT)
+          == 0);
+
+  /* Clear exceptions from past operations.  */
+  feclearexcept (FE_ALL_EXCEPT);
+
+  /* An FE_INVALID exception shall trigger a SIGFPE signal, which by default
+     terminates the program.  */
+  if (sigfpe_on_invalid () < 0)
+    {
+      fputs ("Skipping test: trapping floating-point exceptions are not supported on this machine.\n", stderr);
+      return 77;
+    }
+
+  /* Attempt to set the FE_INVALID exception flag.  */
+  _GL_UNUSED int rc = fesetexceptflag (&saved_flags_1, FE_INVALID);
+  /* On older i386 and on PowerPC, there is no way to implement
+     fesetexceptflag() such that it does not trigger a trap.  fesetexceptflag()
+     is expected to fail in this case.  */
+# if !((defined __i386 || defined _M_IX86) || defined __powerpc__)
+  ASSERT (rc == 0);
+# endif
+
+  /* Do a harmless floating-point operation (since on some CPUs, floating-point
+     exceptions trigger a trap only at the next floating-point operation).  */
+  a = 1.0; b = a + a;
+  al = 1.0L; bl = al + al;
+
+  return 0;
+}
+
+#else
+
+int
+main ()
+{
+  fputs ("Skipping test: feenableexcept or fpsetmask or fp_enable not available\n", stderr);
+  return 77;
+}
+
+#endif
-- 
2.34.1

Reply via email to