These patches add a module 'fenv-exceptions-trapping', that portably implement
the GNU functions
  feenableexcept
  fedisableexcept
  fegetexcept   (a terrible misnomer, btw)

Again, the unit tests uncovered several problems on several platforms.


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

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

        fenv-exceptions-trapping: New module.
        * lib/fenv.in.h (feenableexcept, fedisableexcept, fegetexcept): New
        declarations.
        * lib/fenv-except-trapping.c: New file, based on glibc.
        * m4/fenv_h.m4 (gl_FENV_H): Test also whether fegetexcept is declared.
        * m4/fenv-exceptions-trapping.m4: New file.
        * modules/fenv-exceptions-trapping: New file.
        * doc/glibc-functions/fegetexcept.texi: Mention the new module.
        * doc/glibc-functions/fedisableexcept.texi: Likewise.
        * doc/glibc-functions/feenableexcept.texi: Likewise. Mention the glibc,
        macOS, FreeBSD bugs.

From e5a52e29dfa5a292f9f94c56c9ed2f3c9ff189af Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Tue, 31 Oct 2023 21:46:13 +0100
Subject: [PATCH 1/2] fenv-exceptions-trapping: New module.

* lib/fenv.in.h (feenableexcept, fedisableexcept, fegetexcept): New
declarations.
* lib/fenv-except-trapping.c: New file, based on glibc.
* m4/fenv_h.m4 (gl_FENV_H): Test also whether fegetexcept is declared.
* m4/fenv-exceptions-trapping.m4: New file.
* modules/fenv-exceptions-trapping: New file.
* doc/glibc-functions/fegetexcept.texi: Mention the new module.
* doc/glibc-functions/fedisableexcept.texi: Likewise.
* doc/glibc-functions/feenableexcept.texi: Likewise. Mention the glibc,
macOS, FreeBSD bugs.
---
 ChangeLog                                |  14 +
 doc/glibc-functions/fedisableexcept.texi |   8 +-
 doc/glibc-functions/feenableexcept.texi  |  13 +-
 doc/glibc-functions/fegetexcept.texi     |   8 +-
 lib/fenv-except-trapping.c               | 951 +++++++++++++++++++++++
 lib/fenv.in.h                            |  94 +++
 m4/fenv-exceptions-trapping.m4           | 117 +++
 m4/fenv_h.m4                             |   3 +-
 modules/fenv-exceptions-trapping         |  40 +
 9 files changed, 1236 insertions(+), 12 deletions(-)
 create mode 100644 lib/fenv-except-trapping.c
 create mode 100644 m4/fenv-exceptions-trapping.m4
 create mode 100644 modules/fenv-exceptions-trapping

diff --git a/ChangeLog b/ChangeLog
index 0476828c50..f7d1a2237e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2023-10-31  Bruno Haible  <br...@clisp.org>
+
+	fenv-exceptions-trapping: New module.
+	* lib/fenv.in.h (feenableexcept, fedisableexcept, fegetexcept): New
+	declarations.
+	* lib/fenv-except-trapping.c: New file, based on glibc.
+	* m4/fenv_h.m4 (gl_FENV_H): Test also whether fegetexcept is declared.
+	* m4/fenv-exceptions-trapping.m4: New file.
+	* modules/fenv-exceptions-trapping: New file.
+	* doc/glibc-functions/fegetexcept.texi: Mention the new module.
+	* doc/glibc-functions/fedisableexcept.texi: Likewise.
+	* doc/glibc-functions/feenableexcept.texi: Likewise. Mention the glibc,
+	macOS, FreeBSD bugs.
+
 2023-10-30  Bruno Haible  <br...@clisp.org>
 
 	fenv-exceptions-state-c99: Fix the x86_64 and i386 case.
diff --git a/doc/glibc-functions/fedisableexcept.texi b/doc/glibc-functions/fedisableexcept.texi
index 8b41ae4d87..d26397f44f 100644
--- a/doc/glibc-functions/fedisableexcept.texi
+++ b/doc/glibc-functions/fedisableexcept.texi
@@ -17,15 +17,15 @@
 @uref{https://www.kernel.org/doc/man-pages/online/pages/man3/fedisableexcept.3.html,,man fedisableexcept}.
 @end itemize
 
-Gnulib module: ---
+Gnulib module: fenv-exceptions-trapping
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on some platforms:
+macOS 11.1, musl libc, FreeBSD 5.2.1, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 5.1, HP-UX 11, IRIX 6.5, Solaris 11.4, Cygwin 1.7.7, mingw, MSVC 14, Android 4.4.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This function is missing on some platforms:
-macOS 11.1, FreeBSD 5.2.1, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 5.1, HP-UX 11, IRIX 6.5, Solaris 11.4, Cygwin 1.7.7, mingw, MSVC 14, Android 4.4.
 @end itemize
diff --git a/doc/glibc-functions/feenableexcept.texi b/doc/glibc-functions/feenableexcept.texi
index dfca91f2a7..a858b4d6c6 100644
--- a/doc/glibc-functions/feenableexcept.texi
+++ b/doc/glibc-functions/feenableexcept.texi
@@ -17,15 +17,22 @@
 @uref{https://www.kernel.org/doc/man-pages/online/pages/man3/feenableexcept.3.html,,man feenableexcept}.
 @end itemize
 
-Gnulib module: ---
+Gnulib module: fenv-exceptions-trapping
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on some platforms:
+macOS 11.1, musl libc, FreeBSD 5.2.1, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 5.1, HP-UX 11, IRIX 6.5, Solaris 11.4, Cygwin 1.7.7, mingw, MSVC 14, Android 4.4.
+@item
+This function does not detect failures on
+glibc 2.19/aarch64, FreeBSD 12.2/arm, FreeBSD 12.2/arm64.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
 @item
-This function is missing on some platforms:
-macOS 11.1, FreeBSD 5.2.1, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 5.1, HP-UX 11, IRIX 6.5, Solaris 11.4, Cygwin 1.7.7, mingw, MSVC 14, Android 4.4.
+The resulting signal is @code{SIGILL} instead of @code{SIGFPE}
+on some platforms:
+macOS 12.5/arm64.
 @end itemize
diff --git a/doc/glibc-functions/fegetexcept.texi b/doc/glibc-functions/fegetexcept.texi
index e3e3e43959..eaa1882d83 100644
--- a/doc/glibc-functions/fegetexcept.texi
+++ b/doc/glibc-functions/fegetexcept.texi
@@ -17,15 +17,15 @@
 @uref{https://www.kernel.org/doc/man-pages/online/pages/man3/fegetexcept.3.html,,man fegetexcept}.
 @end itemize
 
-Gnulib module: ---
+Gnulib module: fenv-exceptions-trapping
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function is missing on many non-glibc platforms:
+macOS 11.1, musl libc, FreeBSD 13.0, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 5.1, HP-UX 11, IRIX 6.5, Solaris 11.4, Cygwin 1.7.7, mingw, MSVC 14, Android 4.4.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This function is missing on many non-glibc platforms:
-macOS 11.1, FreeBSD 13.0, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 5.1, HP-UX 11, IRIX 6.5, Solaris 11.4, Cygwin 1.7.7, mingw, MSVC 14, Android 4.4.
 @end itemize
diff --git a/lib/fenv-except-trapping.c b/lib/fenv-except-trapping.c
new file mode 100644
index 0000000000..a86cbd2d39
--- /dev/null
+++ b/lib/fenv-except-trapping.c
@@ -0,0 +1,951 @@
+/* Functions for turning floating-point exceptions into traps (signals).
+   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>/{feenablxcpt.c,fedisblxcpt.c,fegetexcept.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
+feenableexcept (int exceptions)
+{
+  exceptions &= FE_ALL_EXCEPT;
+
+#  if defined _MSC_VER
+
+  exceptions = exceptions_to_x86hardware (exceptions);
+
+  /* Enable the traps only in the SSE unit.  */
+  unsigned int mxcsr, orig_mxcsr;
+  _FPU_GETSSECW (orig_mxcsr);
+  mxcsr = orig_mxcsr & ~(exceptions << 7);
+  if (mxcsr != orig_mxcsr)
+    _FPU_SETSSECW (mxcsr);
+
+  unsigned int trapbits = 0x3f & ~(orig_mxcsr >> 7);
+  return x86hardware_to_exceptions (trapbits);
+
+#  else
+
+  /* Enable the traps in the 387 unit.  */
+  unsigned short fctrl, orig_fctrl;
+  _FPU_GETCW (orig_fctrl);
+  fctrl = orig_fctrl & ~exceptions;
+  if (fctrl != orig_fctrl)
+    _FPU_SETCW (fctrl);
+
+  if (CPU_HAS_SSE ())
+    {
+      /* Enable the traps in the SSE unit as well.  */
+      unsigned int mxcsr, orig_mxcsr;
+      _FPU_GETSSECW (orig_mxcsr);
+      mxcsr = orig_mxcsr & ~(exceptions << 7);
+      if (mxcsr != orig_mxcsr)
+        _FPU_SETSSECW (mxcsr);
+    }
+
+  return FE_ALL_EXCEPT & ~orig_fctrl;
+
+#  endif
+}
+
+int
+fedisableexcept (int exceptions)
+{
+  exceptions &= FE_ALL_EXCEPT;
+
+#  if defined _MSC_VER
+
+  exceptions = exceptions_to_x86hardware (exceptions);
+
+  /* Disable the traps only in the SSE unit.  */
+  unsigned int mxcsr, orig_mxcsr;
+  _FPU_GETSSECW (orig_mxcsr);
+  mxcsr = orig_mxcsr | (exceptions << 7);
+  if (mxcsr != orig_mxcsr)
+    _FPU_SETSSECW (mxcsr);
+
+  unsigned int trapbits = 0x3f & ~(orig_mxcsr >> 7);
+  return x86hardware_to_exceptions (trapbits);
+
+#  else
+
+  /* Disable the traps in the 387 unit.  */
+  unsigned short fctrl, orig_fctrl;
+  _FPU_GETCW (orig_fctrl);
+  fctrl = orig_fctrl | exceptions;
+  if (fctrl != orig_fctrl)
+    _FPU_SETCW (fctrl);
+
+  if (CPU_HAS_SSE ())
+    {
+      /* Disable the traps in the SSE unit as well.  */
+      unsigned int mxcsr, orig_mxcsr;
+      _FPU_GETSSECW (orig_mxcsr);
+      mxcsr = orig_mxcsr | (exceptions << 7);
+      if (mxcsr != orig_mxcsr)
+        _FPU_SETSSECW (mxcsr);
+    }
+
+  return FE_ALL_EXCEPT & ~orig_fctrl;
+
+#  endif
+}
+
+int
+fegetexcept (void)
+{
+#  if defined _MSC_VER
+  /* Look at the trap bits in the SSE unit.  */
+  unsigned int mxcsr;
+  _FPU_GETSSECW (mxcsr);
+  unsigned int trapbits = 0x3f & ~(mxcsr >> 7);
+  return x86hardware_to_exceptions (trapbits);
+#  else
+  /* Look at the trap bits in the 387 unit.  */
+  unsigned short fctrl;
+  _FPU_GETCW (fctrl);
+  return FE_ALL_EXCEPT & ~fctrl;
+#  endif
+}
+
+# elif defined __aarch64__ /* arm64 */
+
+int
+feenableexcept (int exceptions)
+{
+  exceptions &= FE_ALL_EXCEPT;
+
+  unsigned long fpcr, orig_fpcr;
+  _FPU_GETCW (orig_fpcr);
+  fpcr = orig_fpcr | (exceptions << 8);
+  if (fpcr != orig_fpcr)
+    {
+      _FPU_SETCW (fpcr);
+      /* Test whether fpcr was actually changed as desired.  */
+      unsigned long new_fpcr;
+      _FPU_GETCW (new_fpcr);
+      if (new_fpcr != fpcr)
+        return -1;
+    }
+
+  return FE_ALL_EXCEPT & (orig_fpcr >> 8);
+}
+
+int
+fedisableexcept (int exceptions)
+{
+  exceptions &= FE_ALL_EXCEPT;
+
+  unsigned long fpcr, orig_fpcr;
+  _FPU_GETCW (orig_fpcr);
+  fpcr = orig_fpcr & ~(exceptions << 8);
+  if (fpcr != orig_fpcr)
+    {
+      _FPU_SETCW (fpcr);
+      /* Testing whether fpcr was actually changed as desired is not needed
+         here, since we only cleared some bits.  */
+    }
+
+  return FE_ALL_EXCEPT & (orig_fpcr >> 8);
+}
+
+int
+fegetexcept (void)
+{
+  unsigned long fpcr;
+  _FPU_GETCW (fpcr);
+  return FE_ALL_EXCEPT & (fpcr >> 8);
+}
+
+# elif defined __arm__
+
+int
+feenableexcept (int exceptions)
+{
+  exceptions &= FE_ALL_EXCEPT;
+
+#  ifdef __SOFTFP__
+  if (exceptions != 0)
+    return -1;
+  return 0;
+#  else
+  unsigned int fpscr, orig_fpscr;
+  _FPU_GETCW (orig_fpscr);
+  fpscr = orig_fpscr | (exceptions << 8);
+  if (fpscr != orig_fpscr)
+    {
+      _FPU_SETCW (fpscr);
+      /* Test whether fpscr was actually changed as desired.  */
+      unsigned int new_fpscr;
+      _FPU_GETCW (new_fpscr);
+      if (new_fpscr != fpscr)
+        return -1;
+    }
+
+  return FE_ALL_EXCEPT & (orig_fpscr >> 8);
+#  endif
+}
+
+int
+fedisableexcept (int exceptions)
+{
+  exceptions &= FE_ALL_EXCEPT;
+
+#  ifdef __SOFTFP__
+  return 0;
+#  else
+  unsigned int fpscr, orig_fpscr;
+  _FPU_GETCW (orig_fpscr);
+  fpscr = orig_fpscr & ~(exceptions << 8);
+  if (fpscr != orig_fpscr)
+    {
+      _FPU_SETCW (fpscr);
+      /* Testing whether fpscr was actually changed as desired is not needed
+         here, since we only cleared some bits.  */
+    }
+
+  return FE_ALL_EXCEPT & (orig_fpscr >> 8);
+#  endif
+}
+
+int
+fegetexcept (void)
+{
+#  ifdef __SOFTFP__
+  return 0;
+#  else
+  unsigned int fpscr;
+  _FPU_GETCW (fpscr);
+  return FE_ALL_EXCEPT & (fpscr >> 8);
+#  endif
+}
+
+# elif defined __alpha
+
+int
+feenableexcept (int exceptions)
+{
+  exceptions &= FE_ALL_EXCEPT;
+
+  unsigned long swcr, orig_swcr;
+  orig_swcr = __ieee_get_fp_control ();
+  swcr = orig_swcr | ((unsigned long) exceptions >> 16);
+  if (swcr != orig_swcr)
+    __ieee_set_fp_control (swcr);
+
+  return FE_ALL_EXCEPT & (orig_swcr << 16);
+}
+
+int
+fedisableexcept (int exceptions)
+{
+  exceptions &= FE_ALL_EXCEPT;
+
+  unsigned long swcr, orig_swcr;
+  orig_swcr = __ieee_get_fp_control ();
+  swcr = orig_swcr & ~((unsigned long) exceptions >> 16);
+  if (swcr != orig_swcr)
+    __ieee_set_fp_control (swcr);
+
+  return FE_ALL_EXCEPT & (orig_swcr << 16);
+}
+
+int
+fegetexcept (void)
+{
+  unsigned long swcr = __ieee_get_fp_control ();
+  return FE_ALL_EXCEPT & (swcr << 16);
+}
+
+# elif defined __hppa
+
+int
+feenableexcept (int exceptions)
+{
+  exceptions &= FE_ALL_EXCEPT;
+
+  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];
+  /* Set all the relevant bits. */
+  s.halfreg[0] |= (unsigned int) exceptions;
+  if (s.halfreg[0] != old_halfreg0)
+    {
+      /* Store the new status word.  */
+      __asm__ __volatile__ ("fldd 0(%0),%%fr0" : : "r" (&s.fpreg), "m" (s.fpreg) : "%r0");
+    }
+
+  return FE_ALL_EXCEPT & old_halfreg0;
+}
+
+int
+fedisableexcept (int exceptions)
+{
+  exceptions &= FE_ALL_EXCEPT;
+
+  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];
+  /* Clear all the relevant bits. */
+  s.halfreg[0] &= ~ (unsigned int) exceptions;
+  if (s.halfreg[0] != old_halfreg0)
+    {
+      /* Store the new status word.  */
+      __asm__ __volatile__ ("fldd 0(%0),%%fr0" : : "r" (&s.fpreg), "m" (s.fpreg) : "%r0");
+    }
+
+  return FE_ALL_EXCEPT & old_halfreg0;
+}
+
+int
+fegetexcept (void)
+{
+  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");
+
+  return FE_ALL_EXCEPT & s.halfreg[0];
+}
+
+# elif defined __ia64__
+
+int
+feenableexcept (int exceptions)
+{
+  exceptions &= FE_ALL_EXCEPT;
+
+  unsigned long fpsr, orig_fpsr;
+  _FPU_GETCW (orig_fpsr);
+  fpsr = orig_fpsr & ~ (unsigned long) exceptions;
+  if (fpsr != orig_fpsr)
+    _FPU_SETCW (fpsr);
+
+  return FE_ALL_EXCEPT & ~orig_fpsr;
+}
+
+int
+fedisableexcept (int exceptions)
+{
+  exceptions &= FE_ALL_EXCEPT;
+
+  unsigned long fpsr, orig_fpsr;
+  _FPU_GETCW (orig_fpsr);
+  fpsr = orig_fpsr | (unsigned long) exceptions;
+  if (fpsr != orig_fpsr)
+    _FPU_SETCW (fpsr);
+
+  return FE_ALL_EXCEPT & ~orig_fpsr;
+}
+
+int
+fegetexcept (void)
+{
+  unsigned long fpsr;
+  _FPU_GETCW (fpsr);
+  return FE_ALL_EXCEPT & ~fpsr;
+}
+
+# elif defined __m68k__
+
+int
+feenableexcept (int exceptions)
+{
+  exceptions &= FE_ALL_EXCEPT;
+
+  unsigned int fpcr, orig_fpcr;
+  _FPU_GETCW (orig_fpcr);
+  fpcr = orig_fpcr | ((exceptions << 6) | (exceptions & FE_INVALID ? 0xc000 : 0));
+  if (fpcr != orig_fpcr)
+    _FPU_SETCW (fpcr);
+
+  return FE_ALL_EXCEPT & (orig_fpcr >> 6);
+}
+
+int
+fedisableexcept (int exceptions)
+{
+  exceptions &= FE_ALL_EXCEPT;
+
+  unsigned int fpcr, orig_fpcr;
+  _FPU_GETCW (orig_fpcr);
+  fpcr = orig_fpcr & ~ ((exceptions << 6) | (exceptions & FE_INVALID ? 0xc000 : 0));
+  if (fpcr != orig_fpcr)
+    _FPU_SETCW (fpcr);
+
+  return FE_ALL_EXCEPT & (orig_fpcr >> 6);
+}
+
+int
+fegetexcept (void)
+{
+  unsigned int fpcr;
+  _FPU_GETCW (fpcr);
+  return FE_ALL_EXCEPT & (fpcr >> 6);
+}
+
+# elif defined __mips__
+
+int
+feenableexcept (int exceptions)
+{
+  exceptions &= FE_ALL_EXCEPT;
+
+  unsigned int fcsr, orig_fcsr;
+  _FPU_GETCW (orig_fcsr);
+  fcsr = orig_fcsr | (exceptions << 5);
+  if (fcsr != orig_fcsr)
+    _FPU_SETCW (fcsr);
+
+  return FE_ALL_EXCEPT & (orig_fcsr >> 5);
+}
+
+int
+fedisableexcept (int exceptions)
+{
+  exceptions &= FE_ALL_EXCEPT;
+
+  unsigned int fcsr, orig_fcsr;
+  _FPU_GETCW (orig_fcsr);
+  fcsr = orig_fcsr & ~ (exceptions << 5);
+  if (fcsr != orig_fcsr)
+    _FPU_SETCW (fcsr);
+
+  return FE_ALL_EXCEPT & (orig_fcsr >> 5);
+}
+
+int
+fegetexcept (void)
+{
+  unsigned int fcsr;
+  _FPU_GETCW (fcsr);
+  return FE_ALL_EXCEPT & (fcsr >> 5);
+}
+
+# elif defined __loongarch__
+
+int
+feenableexcept (int exceptions)
+{
+  exceptions &= FE_ALL_EXCEPT;
+
+  unsigned int fcsr, orig_fcsr;
+  _FPU_GETCW (orig_fcsr);
+  fcsr = orig_fcsr | (exceptions >> 16);
+  if (fcsr != orig_fcsr)
+    _FPU_SETCW (fcsr);
+
+  return FE_ALL_EXCEPT & (orig_fcsr << 16);
+}
+
+int
+fedisableexcept (int exceptions)
+{
+  exceptions &= FE_ALL_EXCEPT;
+
+  unsigned int fcsr, orig_fcsr;
+  _FPU_GETCW (orig_fcsr);
+  fcsr = orig_fcsr & ~ (exceptions >> 16);
+  if (fcsr != orig_fcsr)
+    _FPU_SETCW (fcsr);
+
+  return FE_ALL_EXCEPT & (orig_fcsr << 16);
+}
+
+int
+fegetexcept (void)
+{
+  unsigned int fcsr;
+  _FPU_GETCW (fcsr);
+  return FE_ALL_EXCEPT & (fcsr << 16);
+}
+
+# elif defined __powerpc__
+
+#  if defined __linux__
+#   include <sys/prctl.h>
+#  elif defined _AIX
+#   include <fptrap.h>
+#  endif
+
+int
+feenableexcept (int exceptions)
+{
+  exceptions &= FE_ALL_EXCEPT;
+
+  union { unsigned long long u; double f; } memenv, orig_memenv;
+  _FPU_GETCW_AS_DOUBLE (memenv.f);
+  orig_memenv = memenv;
+
+  memenv.u |= ((unsigned int) exceptions >> 22);
+
+  if (!(memenv.u == orig_memenv.u))
+    {
+      if ((orig_memenv.u & 0x000000f7) == 0 && (memenv.u & 0x000000f7) != 0)
+        {
+          /* Put the thread into precise trapping mode.  */
+#  if defined __linux__ || defined __NetBSD__
+          prctl (PR_SET_FPEXC, PR_FP_EXC_PRECISE);
+#  elif defined _AIX
+          /* Documentation: <https://www.ibm.com/docs/en/aix/7.3?topic=f-fp-trap-subroutine>  */
+          fp_trap (FP_TRAP_SYNC);
+#  endif
+        }
+
+      _FPU_SETCW_AS_DOUBLE (memenv.f);
+    }
+
+  return FE_ALL_EXCEPT & ((unsigned int) orig_memenv.u << 22);
+}
+
+int
+fedisableexcept (int exceptions)
+{
+  exceptions &= FE_ALL_EXCEPT;
+
+  union { unsigned long long u; double f; } memenv, orig_memenv;
+  _FPU_GETCW_AS_DOUBLE (memenv.f);
+  orig_memenv = memenv;
+
+  memenv.u &= ~ ((unsigned int) exceptions >> 22);
+
+  if (!(memenv.u == orig_memenv.u))
+    {
+      _FPU_SETCW_AS_DOUBLE (memenv.f);
+
+      if ((orig_memenv.u & 0x000000f7) != 0 && (memenv.u & 0x000000f7) == 0)
+        {
+          /* Put the thread into no-trapping mode.  */
+#  if defined __linux__ || defined __NetBSD__
+          prctl (PR_SET_FPEXC, PR_FP_EXC_DISABLED);
+#  elif defined _AIX
+          /* Documentation: <https://www.ibm.com/docs/en/aix/7.3?topic=f-fp-trap-subroutine>  */
+          fp_trap (FP_TRAP_OFF);
+#  endif
+        }
+    }
+
+  return FE_ALL_EXCEPT & ((unsigned int) orig_memenv.u << 22);
+}
+
+int
+fegetexcept (void)
+{
+  union { unsigned long long u; double f; } memenv;
+  _FPU_GETCW_AS_DOUBLE (memenv.f);
+
+  return FE_ALL_EXCEPT & ((unsigned int) memenv.u << 22);
+}
+
+# elif defined __riscv
+
+int
+feenableexcept (int exceptions)
+{
+  exceptions &= FE_ALL_EXCEPT;
+
+  if (exceptions != 0)
+    return -1;
+  return 0;
+}
+
+int
+fedisableexcept (int exceptions)
+{
+  return 0;
+}
+
+int
+fegetexcept (void)
+{
+  return 0;
+}
+
+# elif defined __s390__ || defined __s390x__
+
+int
+feenableexcept (int exceptions)
+{
+  exceptions &= FE_ALL_EXCEPT;
+
+  unsigned int fpc, orig_fpc;
+  _FPU_GETCW (orig_fpc);
+#  if FE_INEXACT == 8 /* glibc compatible FE_* values */
+  fpc = orig_fpc | ((unsigned int) exceptions << 24);
+#  else /* musl libc compatible FE_* values */
+  fpc = orig_fpc | ((unsigned int) exceptions << 8);
+#  endif
+  if (fpc != orig_fpc)
+    _FPU_SETCW (fpc);
+
+#  if FE_INEXACT == 8 /* glibc compatible FE_* values */
+  return FE_ALL_EXCEPT & (orig_fpc >> 24);
+#  else /* musl libc compatible FE_* values */
+  return FE_ALL_EXCEPT & (orig_fpc >> 8);
+#  endif
+}
+
+int
+fedisableexcept (int exceptions)
+{
+  exceptions &= FE_ALL_EXCEPT;
+
+  unsigned int fpc, orig_fpc;
+  _FPU_GETCW (orig_fpc);
+#  if FE_INEXACT == 8 /* glibc compatible FE_* values */
+  fpc = orig_fpc & ~((unsigned int) exceptions << 24);
+#  else /* musl libc compatible FE_* values */
+  fpc = orig_fpc & ~((unsigned int) exceptions << 8);
+#  endif
+  if (fpc != orig_fpc)
+    _FPU_SETCW (fpc);
+
+#  if FE_INEXACT == 8 /* glibc compatible FE_* values */
+  return FE_ALL_EXCEPT & (orig_fpc >> 24);
+#  else /* musl libc compatible FE_* values */
+  return FE_ALL_EXCEPT & (orig_fpc >> 8);
+#  endif
+}
+
+int
+fegetexcept (void)
+{
+  unsigned int fpc;
+  _FPU_GETCW (fpc);
+#  if FE_INEXACT == 8 /* glibc compatible FE_* values */
+  return FE_ALL_EXCEPT & (fpc >> 24);
+#  else /* musl libc compatible FE_* values */
+  return FE_ALL_EXCEPT & (fpc >> 8);
+#  endif
+}
+
+# elif defined __sh__
+
+int
+feenableexcept (int exceptions)
+{
+  exceptions &= FE_ALL_EXCEPT;
+
+  unsigned int fpscr, orig_fpscr;
+  _FPU_GETCW (orig_fpscr);
+  fpscr = orig_fpscr | ((unsigned int) exceptions << 5);
+  if (fpscr != orig_fpscr)
+    _FPU_SETCW (fpscr);
+
+  return FE_ALL_EXCEPT & (orig_fpscr >> 5);
+}
+
+int
+fedisableexcept (int exceptions)
+{
+  exceptions &= FE_ALL_EXCEPT;
+
+  unsigned int fpscr, orig_fpscr;
+  _FPU_GETCW (orig_fpscr);
+  fpscr = orig_fpscr & ~ ((unsigned int) exceptions << 5);
+  if (fpscr != orig_fpscr)
+    _FPU_SETCW (fpscr);
+
+  return FE_ALL_EXCEPT & (orig_fpscr >> 5);
+}
+
+int
+fegetexcept (void)
+{
+  unsigned int fpscr;
+  _FPU_GETCW (fpscr);
+  return FE_ALL_EXCEPT & (fpscr >> 5);
+}
+
+# elif defined __sparc
+
+int
+feenableexcept (int exceptions)
+{
+  exceptions &= FE_ALL_EXCEPT;
+
+  unsigned long fsr, orig_fsr;
+  _FPU_GETCW (orig_fsr);
+#  if FE_INEXACT == 32 /* glibc compatible FE_* values */
+  fsr = orig_fsr | (exceptions << 18);
+#  else /* Solaris compatible FE_* values */
+  fsr = orig_fsr | (exceptions << 23);
+#  endif
+  if (fsr != orig_fsr)
+    _FPU_SETCW (fsr);
+
+#  if FE_INEXACT == 32 /* glibc compatible FE_* values */
+  return FE_ALL_EXCEPT & (orig_fsr >> 18);
+#  else /* Solaris compatible FE_* values */
+  return FE_ALL_EXCEPT & (orig_fsr >> 23);
+#  endif
+}
+
+int
+fedisableexcept (int exceptions)
+{
+  exceptions &= FE_ALL_EXCEPT;
+
+  unsigned long fsr, orig_fsr;
+  _FPU_GETCW (orig_fsr);
+#  if FE_INEXACT == 32 /* glibc compatible FE_* values */
+  fsr = orig_fsr & ~(exceptions << 18);
+#  else /* Solaris compatible FE_* values */
+  fsr = orig_fsr & ~(exceptions << 23);
+#  endif
+  if (fsr != orig_fsr)
+    _FPU_SETCW (fsr);
+
+#  if FE_INEXACT == 32 /* glibc compatible FE_* values */
+  return FE_ALL_EXCEPT & (orig_fsr >> 18);
+#  else /* Solaris compatible FE_* values */
+  return FE_ALL_EXCEPT & (orig_fsr >> 23);
+#  endif
+}
+
+int
+fegetexcept (void)
+{
+  unsigned long fsr;
+  _FPU_GETCW (fsr);
+#  if FE_INEXACT == 32 /* glibc compatible FE_* values */
+  return FE_ALL_EXCEPT & (fsr >> 18);
+#  else /* Solaris compatible FE_* values */
+  return FE_ALL_EXCEPT & (fsr >> 23);
+#  endif
+}
+
+# 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_FPSETMASK
+/* FreeBSD ≥ 3.1, NetBSD ≥ 1.1, OpenBSD, IRIX, Solaris, Minix ≥ 3.2.  */
+
+/* Get fpgetmask, fpsetmask.  */
+#  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
+
+static fp_except_t
+exceptions_to_mask (int exceptions)
+{
+  fp_except_t m = 0;
+  if (exceptions & FE_INVALID)
+    m |= FP_X_INV;
+  if (exceptions & FE_DIVBYZERO)
+    m |= FP_X_DZ;
+  if (exceptions & FE_OVERFLOW)
+    m |= FP_X_OFL;
+  if (exceptions & FE_UNDERFLOW)
+    m |= FP_X_UFL;
+  if (exceptions & FE_INEXACT)
+    m |= FP_X_IMP;
+  return m;
+}
+
+static int
+mask_to_exceptions (fp_except_t m)
+{
+  int exceptions = 0;
+  if (m & FP_X_INV)
+    exceptions |= FE_INVALID;
+  if (m & FP_X_DZ)
+    exceptions |= FE_DIVBYZERO;
+  if (m & FP_X_OFL)
+    exceptions |= FE_OVERFLOW;
+  if (m & FP_X_UFL)
+    exceptions |= FE_UNDERFLOW;
+  if (m & FP_X_IMP)
+    exceptions |= FE_INEXACT;
+  return exceptions;
+}
+
+int
+feenableexcept (int exceptions)
+{
+  exceptions &= FE_ALL_EXCEPT;
+
+  fp_except_t trapbits = fpgetmask ();
+  fpsetmask (trapbits | exceptions_to_mask (exceptions));
+  return mask_to_exceptions (trapbits);
+}
+
+int
+fedisableexcept (int exceptions)
+{
+  exceptions &= FE_ALL_EXCEPT;
+
+  fp_except_t trapbits = fpgetmask ();
+  fpsetmask (trapbits & ~ exceptions_to_mask (exceptions));
+  return mask_to_exceptions (trapbits);
+}
+
+int
+fegetexcept (void)
+{
+  fp_except_t trapbits = fpgetmask ();
+  return mask_to_exceptions (trapbits);
+}
+
+# elif defined _AIX && defined __powerpc__ /* AIX */
+
+#  include <fptrap.h>
+/* Documentation:
+   <https://www.ibm.com/docs/en/aix/7.3?topic=f-fp-any-enable-fp-is-enabled-fp-enable-all-fp-enable-fp-disable-all-fp-disable-subroutine>
+   <https://www.ibm.com/docs/en/aix/7.3?topic=f-fp-trap-subroutine>  */
+
+/* Convert from an 'int exceptions' to an fptrap_t.  */
+#  if 0 /* Unoptimized */
+#   define exceptions_to_fptrap(exceptions) \
+      (  ((exceptions) & FE_INVALID   ? TRP_INVALID     : 0) \
+       | ((exceptions) & FE_DIVBYZERO ? TRP_DIV_BY_ZERO : 0) \
+       | ((exceptions) & FE_OVERFLOW  ? TRP_OVERFLOW    : 0) \
+       | ((exceptions) & FE_UNDERFLOW ? TRP_UNDERFLOW   : 0) \
+       | ((exceptions) & FE_INEXACT   ? TRP_INEXACT     : 0))
+#  else /* Optimized */
+#   define exceptions_to_fptrap(exceptions) \
+      (((unsigned int) (exceptions) & FE_ALL_EXCEPT) >> 22)
+#  endif
+
+int
+feenableexcept (int exceptions)
+{
+  exceptions &= FE_ALL_EXCEPT;
+
+  int prev_enabled = fegetexcept ();
+  if (exceptions != 0)
+    {
+      fp_enable (exceptions_to_fptrap (exceptions));
+      if (prev_enabled == 0 /* && fegetexcept () != 0 */)
+        /* Enable precise trapping mode.  */
+        fp_trap (FP_TRAP_SYNC);
+    }
+  return prev_enabled;
+}
+
+int
+fedisableexcept (int exceptions)
+{
+  exceptions &= FE_ALL_EXCEPT;
+
+  int prev_enabled = fegetexcept ();
+  if (exceptions != 0)
+    {
+      fp_disable (exceptions_to_fptrap (exceptions));
+      if (prev_enabled != 0 && fegetexcept () == 0)
+        /* Disable precise trapping mode.  */
+        fp_trap (FP_TRAP_OFF);
+    }
+  return prev_enabled;
+}
+
+int
+fegetexcept (void)
+{
+  return fegetexcept_impl ();
+}
+
+# elif HAVE_FESETTRAPENABLE
+/* HP-UX, QNX */
+
+int
+feenableexcept (int exceptions)
+{
+  exceptions &= FE_ALL_EXCEPT;
+
+  int prev_enabled = fegettrapenable ();
+  if (exceptions != 0)
+    fesettrapenable (prev_enabled | exceptions);
+  return prev_enabled;
+}
+
+int
+fedisableexcept (int exceptions)
+{
+  exceptions &= FE_ALL_EXCEPT;
+
+  int prev_enabled = fegettrapenable ();
+  if (exceptions != 0)
+    fesettrapenable (prev_enabled & ~exceptions);
+  return prev_enabled;
+}
+
+int
+fegetexcept (void)
+{
+  return fegettrapenable ();
+}
+
+# else
+
+#  define NEED_FALLBACK 1
+
+# endif
+
+#endif
+
+#if NEED_FALLBACK
+
+/* A dummy fallback.  */
+
+int
+feenableexcept (int exceptions)
+{
+  exceptions &= FE_ALL_EXCEPT;
+  if (exceptions != 0)
+    return -1;
+  return 0;
+}
+
+int
+fedisableexcept (int exceptions)
+{
+  return 0;
+}
+
+int
+fegetexcept (void)
+{
+  return 0;
+}
+
+#endif
diff --git a/lib/fenv.in.h b/lib/fenv.in.h
index 2b2f770e2c..4fd5ea2f1a 100644
--- a/lib/fenv.in.h
+++ b/lib/fenv.in.h
@@ -542,6 +542,100 @@ _GL_WARN_ON_USE (fesetexcept, "fesetexcept is unportable - "
 #endif
 
 
+/* GNU extensions.  */
+
+#if @GNULIB_FEENABLEEXCEPT@
+/* Enables trapping for the floating-point exceptions denoted by the bit mask
+   EXCEPTIONS.
+   Returns the bit mask of floating-point exceptions for which trapping was
+   enabled before the call, or -1 upon failure.
+   Note: This function is a misnomer.  It does not enable the specified
+   floating-point exceptions; it enables *trapping* on them.  It should better
+   be called 'feenabletraps'.  */
+# if @REPLACE_FEENABLEEXCEPT@ || (!@HAVE_FEENABLEEXCEPT@ && defined __FreeBSD__) /* has an inline definition */
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   undef feenableexcept
+#   define feenableexcept rpl_feenableexcept
+#  endif
+_GL_FUNCDECL_RPL (feenableexcept, int, (int exceptions));
+_GL_CXXALIAS_RPL (feenableexcept, int, (int exceptions));
+# else
+#  if !@HAVE_FEENABLEEXCEPT@
+_GL_FUNCDECL_SYS (feenableexcept, int, (int exceptions));
+#  endif
+_GL_CXXALIAS_SYS (feenableexcept, int, (int exceptions));
+# endif
+_GL_CXXALIASWARN (feenableexcept);
+#elif defined GNULIB_POSIXCHECK
+# undef feenableexcept
+# if HAVE_RAW_DECL_FEENABLEEXCEPT
+_GL_WARN_ON_USE (feenableexcept, "feenableexcept is unportable - "
+                 "use gnulib module fenv-exceptions-trapping for portability");
+# endif
+#endif
+
+#if @GNULIB_FEDISABLEEXCEPT@
+/* Disables trapping for the floating-point exceptions denoted by the bit mask
+   EXCEPTIONS.
+   Returns the bit mask of floating-point exceptions for which trapping was
+   enabled before the call, or -1 upon failure.
+   Note: This function is a misnomer.  It does not disable the specified
+   floating-point exceptions; it disables *trapping* on them.  It should better
+   be called 'fedisabletraps'.  */
+# if @REPLACE_FEDISABLEEXCEPT@ || (!@HAVE_FEDISABLEEXCEPT@ && defined __FreeBSD__) /* has an inline definition */
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   undef fedisableexcept
+#   define fedisableexcept rpl_fedisableexcept
+#  endif
+_GL_FUNCDECL_RPL (fedisableexcept, int, (int exceptions));
+_GL_CXXALIAS_RPL (fedisableexcept, int, (int exceptions));
+# else
+#  if !@HAVE_FEDISABLEEXCEPT@
+_GL_FUNCDECL_SYS (fedisableexcept, int, (int exceptions));
+#  endif
+_GL_CXXALIAS_SYS (fedisableexcept, int, (int exceptions));
+# endif
+_GL_CXXALIASWARN (fedisableexcept);
+#elif defined GNULIB_POSIXCHECK
+# undef fedisableexcept
+# if HAVE_RAW_DECL_FEDISABLEEXCEPT
+_GL_WARN_ON_USE (fedisableexcept, "fedisableexcept is unportable - "
+                 "use gnulib module fenv-exceptions-trapping for portability");
+# endif
+#endif
+
+#if @GNULIB_FEGETEXCEPT@
+/* Returns the bit mask of floating-point exceptions for which trapping is
+   enabled.
+   Note: This function is an even bigger misnomer:
+     - It does not test for the specified floating-point exceptions;
+       fetestexcept() does that.  It tests whether *trapping* on these
+       exceptions is enabled.
+     - It is in no way the opposite of fesetexcept().
+   It should better be called 'fegettraps'.  */
+# if @REPLACE_FEGETEXCEPT@ || (!@HAVE_FEGETEXCEPT@ && defined __FreeBSD__) /* has an inline definition */
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   undef fegetexcept
+#   define fegetexcept rpl_fegetexcept
+#  endif
+_GL_FUNCDECL_RPL (fegetexcept, int, (void));
+_GL_CXXALIAS_RPL (fegetexcept, int, (void));
+# else
+#  if !@HAVE_FEGETEXCEPT@
+_GL_FUNCDECL_SYS (fegetexcept, int, (void));
+#  endif
+_GL_CXXALIAS_SYS (fegetexcept, int, (void));
+# endif
+_GL_CXXALIASWARN (fegetexcept);
+#elif defined GNULIB_POSIXCHECK
+# undef fegetexcept
+# if HAVE_RAW_DECL_FEGETEXCEPT
+_GL_WARN_ON_USE (fegetexcept, "fegetexcept is unportable - "
+                 "use gnulib module fenv-exceptions-trapping for portability");
+# endif
+#endif
+
+
 /* ISO C 99 § 7.6.2 Floating-point exceptions
    ISO C 23 § 7.6.4 Floating-point exceptions
    API with fexcept_t.
diff --git a/m4/fenv-exceptions-trapping.m4 b/m4/fenv-exceptions-trapping.m4
new file mode 100644
index 0000000000..f85e356a88
--- /dev/null
+++ b/m4/fenv-exceptions-trapping.m4
@@ -0,0 +1,117 @@
+# fenv-exceptions-trapping.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_ONCE([gl_FENV_EXCEPTIONS_TRAPPING],
+[
+  AC_REQUIRE([gl_FENV_H_DEFAULTS])
+  dnl Persuade glibc <fenv.h> to declare feenableexcept().
+  AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
+  AC_REQUIRE([AC_CANONICAL_HOST])
+
+  gl_MATHFUNC([feenableexcept], [int], [(int)], [#include <fenv.h>])
+  if test $gl_cv_func_feenableexcept_no_libm = yes \
+     || test $gl_cv_func_feenableexcept_in_libm = yes; then
+    dnl On glibc 2.19/aarch64, feenableexcept does not detect failures.
+    dnl Fixed through
+    dnl <https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=302949e2940a9da3f6364a1574619e621b7e1e71>.
+    dnl Similarly on FreeBSD 12.2/arm and FreeBSD 12.2/arm64.
+    case "$host" in
+      aarch64*-*-linux*)
+        AC_CACHE_CHECK([whether feenableexcept works],
+          [gl_cv_func_feenableexcept_works],
+          [AC_COMPILE_IFELSE(
+             [AC_LANG_PROGRAM([[
+                #include <string.h> /* for __GNU_LIBRARY__ */
+                #ifdef __GNU_LIBRARY__
+                 #include <features.h>
+                 #if __GLIBC__ == 2 && __GLIBC_MINOR__ <= 19
+                  Unlucky user
+                 #endif
+                #endif
+                ]],
+                [])],
+             [gl_cv_func_feenableexcept_works="guessing yes"],
+             [gl_cv_func_feenableexcept_works="guessing no"])
+          ])
+        case "$gl_cv_func_feenableexcept_works" in
+          *yes) ;;
+          *) REPLACE_FEENABLEEXCEPT=1 ;;
+        esac
+        ;;
+      arm*-*-freebsd* | aarch64*-*-freebsd*)
+        REPLACE_FEENABLEEXCEPT=1
+        ;;
+    esac
+    if test $REPLACE_FEENABLEEXCEPT = 1; then
+      dnl Two more functions are defined in the same compilation unit.
+      dnl Override them as well.
+      REPLACE_FEDISABLEEXCEPT=1
+      REPLACE_FEGETEXCEPT=1
+      FENV_EXCEPTIONS_TRAPPING_LIBM=
+    else
+      dnl It needs linking with -lm on glibc.
+      if test $gl_cv_func_feenableexcept_no_libm = yes; then
+        FENV_EXCEPTIONS_TRAPPING_LIBM=
+      else
+        FENV_EXCEPTIONS_TRAPPING_LIBM=-lm
+      fi
+    fi
+  else
+    HAVE_FEENABLEEXCEPT=0
+    HAVE_FEDISABLEEXCEPT=0
+    HAVE_FEGETEXCEPT=0
+    case "$host" in
+      alpha*-*-linux*)
+        dnl For feenableexcept, which we take from libm.
+        FENV_EXCEPTIONS_TRAPPING_LIBM=-lm
+        ;;
+      *)
+        FENV_EXCEPTIONS_TRAPPING_LIBM=
+        ;;
+    esac
+  fi
+  if test $HAVE_FEENABLEEXCEPT = 0 || test $REPLACE_FEENABLEEXCEPT = 1; then
+    gl_PREREQ_FENV_EXCEPTIONS_TRAPPING
+    dnl Possibly need -lm for fpgetmask(), fpsetmask().
+    if test $gl_cv_func_fpsetmask_no_libm = no \
+       && test $gl_cv_func_fpsetmask_in_libm = yes \
+       && test -z "$FENV_EXCEPTIONS_TRAPPING_LIBM"; then
+      FENV_EXCEPTIONS_TRAPPING_LIBM=-lm
+    fi
+    dnl Possibly need -lm for fegettrapenable(), fesettrapenable().
+    if test $gl_cv_func_fesettrapenable_no_libm = no \
+       && test $gl_cv_func_fesettrapenable_in_libm = yes \
+       && test -z "$FENV_EXCEPTIONS_TRAPPING_LIBM"; then
+      FENV_EXCEPTIONS_TRAPPING_LIBM=-lm
+    fi
+  fi
+  AC_SUBST([FENV_EXCEPTIONS_TRAPPING_LIBM])
+])
+
+dnl Prerequisites of lib/fenv-except-trapping.c.
+AC_DEFUN([gl_PREREQ_FENV_EXCEPTIONS_TRAPPING],
+[
+  gl_MATHFUNC([fpsetmask], [fp_except_t], [(fp_except_t)],
+    [#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
+    ])
+  if test $gl_cv_func_fpsetmask_no_libm = yes \
+     || test $gl_cv_func_fpsetmask_in_libm = yes; then
+    AC_DEFINE([HAVE_FPSETMASK], [1],
+      [Define to 1 if you have the 'fpsetmask' function.])
+  fi
+
+  gl_MATHFUNC([fesettrapenable], [int], [(int)], [#include <fenv.h>])
+  if test $gl_cv_func_fesettrapenable_no_libm = yes \
+     || test $gl_cv_func_fesettrapenable_in_libm = yes; then
+    AC_DEFINE([HAVE_FESETTRAPENABLE], [1],
+      [Define to 1 if you have the 'fesettrapenable' function.])
+  fi
+])
diff --git a/m4/fenv_h.m4 b/m4/fenv_h.m4
index 9e7c809d06..853f66e5a9 100644
--- a/m4/fenv_h.m4
+++ b/m4/fenv_h.m4
@@ -22,7 +22,8 @@ AC_DEFUN_ONCE([gl_FENV_H]
   dnl corresponding gnulib module is not in use, and which is not
   dnl guaranteed by C99.
   gl_WARN_ON_USE_PREPARE([[#include <fenv.h>
-    ]], [fedisableexcept feenableexcept fesetexcept fetestexceptflag])
+    ]], [fedisableexcept feenableexcept fegetexcept fesetexcept
+    fetestexceptflag])
 ])
 
 # gl_FENV_MODULE_INDICATOR([modulename])
diff --git a/modules/fenv-exceptions-trapping b/modules/fenv-exceptions-trapping
new file mode 100644
index 0000000000..7ce55fcbd6
--- /dev/null
+++ b/modules/fenv-exceptions-trapping
@@ -0,0 +1,40 @@
+Description:
+Functions for turning floating-point exceptions into traps (signals):
+feenableexcept, fedisableexcept, fegetexcept.
+
+Files:
+lib/fenv-except-trapping.c
+lib/fenv-private.h
+m4/fenv-exceptions-trapping.m4
+m4/mathfunc.m4
+m4/musl.m4
+
+Depends-on:
+fenv
+extensions
+
+configure.ac:
+gl_FENV_EXCEPTIONS_TRAPPING
+gl_CONDITIONAL([GL_COND_OBJ_FENV_EXCEPTIONS_TRAPPING],
+               [test $HAVE_FEENABLEEXCEPT = 0 || test $REPLACE_FEENABLEEXCEPT = 1])
+gl_FENV_MODULE_INDICATOR([feenableexcept])
+gl_FENV_MODULE_INDICATOR([fedisableexcept])
+gl_FENV_MODULE_INDICATOR([fegetexcept])
+gl_MODULE_INDICATOR([feenableexcept])
+
+Makefile.am:
+if GL_COND_OBJ_FENV_EXCEPTIONS_TRAPPING
+lib_SOURCES += fenv-except-trapping.c
+endif
+
+Include:
+#include <fenv.h>
+
+Link:
+$(FENV_EXCEPTIONS_TRAPPING_LIBM)
+
+License:
+LGPLv2+
+
+Maintainer:
+all
-- 
2.34.1

>From e60ee371bb935823d18c98ad9cefe440eb4a43c7 Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Tue, 31 Oct 2023 21:54:49 +0100
Subject: [PATCH 2/2] fenv-exceptions-trapping: Add tests.

* tests/test-fenv-except-trapping-1.c: New file.
* tests/test-fenv-except-trapping-2.sh: New file.
* tests/test-fenv-except-trapping-2.c: New file.
* modules/fenv-exceptions-trapping-tests: New file.
---
 ChangeLog                              |   6 +
 modules/fenv-exceptions-trapping-tests |  25 ++
 tests/test-fenv-except-trapping-1.c    |  69 ++++
 tests/test-fenv-except-trapping-2.c    | 488 +++++++++++++++++++++++++
 tests/test-fenv-except-trapping-2.sh   |  33 ++
 5 files changed, 621 insertions(+)
 create mode 100644 modules/fenv-exceptions-trapping-tests
 create mode 100644 tests/test-fenv-except-trapping-1.c
 create mode 100644 tests/test-fenv-except-trapping-2.c
 create mode 100755 tests/test-fenv-except-trapping-2.sh

diff --git a/ChangeLog b/ChangeLog
index f7d1a2237e..3b327c4c3d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,11 @@
 2023-10-31  Bruno Haible  <br...@clisp.org>
 
+	fenv-exceptions-trapping: Add tests.
+	* tests/test-fenv-except-trapping-1.c: New file.
+	* tests/test-fenv-except-trapping-2.sh: New file.
+	* tests/test-fenv-except-trapping-2.c: New file.
+	* modules/fenv-exceptions-trapping-tests: New file.
+
 	fenv-exceptions-trapping: New module.
 	* lib/fenv.in.h (feenableexcept, fedisableexcept, fegetexcept): New
 	declarations.
diff --git a/modules/fenv-exceptions-trapping-tests b/modules/fenv-exceptions-trapping-tests
new file mode 100644
index 0000000000..21f178461f
--- /dev/null
+++ b/modules/fenv-exceptions-trapping-tests
@@ -0,0 +1,25 @@
+Files:
+tests/test-fenv-except-trapping-1.c
+tests/test-fenv-except-trapping-2.sh
+tests/test-fenv-except-trapping-2.c
+tests/infinity.h
+tests/macros.h
+m4/musl.m4
+
+Depends-on:
+fenv-exceptions-tracking-c99
+nan
+snan
+
+configure.ac:
+gl_MUSL_LIBC
+
+Makefile.am:
+TESTS += \
+  test-fenv-except-trapping-1 \
+  test-fenv-except-trapping-2.sh
+check_PROGRAMS += \
+  test-fenv-except-trapping-1 \
+  test-fenv-except-trapping-2
+test_fenv_except_trapping_1_LDADD = $(LDADD) @FENV_EXCEPTIONS_TRAPPING_LIBM@
+test_fenv_except_trapping_2_LDADD = $(LDADD) @FENV_EXCEPTIONS_TRAPPING_LIBM@ @FENV_EXCEPTIONS_TRACKING_LIBM@
diff --git a/tests/test-fenv-except-trapping-1.c b/tests/test-fenv-except-trapping-1.c
new file mode 100644
index 0000000000..7c67a06ec9
--- /dev/null
+++ b/tests/test-fenv-except-trapping-1.c
@@ -0,0 +1,69 @@
+/* Test of turning floating-point exceptions into traps (signals).
+   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 "macros.h"
+
+/* Check of the Boole-an algebra.  */
+
+/* Converts 5 bits to an exceptions bit mask.  */
+static int
+uint_to_exceptions (unsigned int a)
+{
+  return (a & 0x01 ? FE_INVALID : 0)
+         | (a & 0x02 ? FE_DIVBYZERO : 0)
+         | (a & 0x04 ? FE_OVERFLOW : 0)
+         | (a & 0x08 ? FE_UNDERFLOW : 0)
+         | (a & 0x10 ? FE_INEXACT : 0);
+}
+
+int
+main ()
+{
+  unsigned int a, b;
+
+  /* Run through all possible valid arguments to feenableexcept and
+     fedisableexcept.  */
+  for (a = 0; a < 0x20; a++)
+    for (b = 0; b < 0x20; b++)
+      {
+        unsigned int c = a & ~b;
+
+        if (fedisableexcept (FE_ALL_EXCEPT) == -1
+            || feenableexcept (uint_to_exceptions (a)) == -1)
+          {
+            fputs ("Skipping test: trapping floating-point exceptions are not supported on this machine.\n", stderr);
+            return 77;
+          }
+        ASSERT (fedisableexcept (uint_to_exceptions (b))
+                == uint_to_exceptions (a));
+        /* Check fegetexcept.  */
+        ASSERT (fegetexcept () == uint_to_exceptions (c));
+        /* Check the return value of feenableexcept.  It should be consistent
+           with fegetexcept.  */
+        ASSERT (feenableexcept (0) == uint_to_exceptions (c));
+      }
+
+  return 0;
+}
diff --git a/tests/test-fenv-except-trapping-2.c b/tests/test-fenv-except-trapping-2.c
new file mode 100644
index 0000000000..18f42b89c2
--- /dev/null
+++ b/tests/test-fenv-except-trapping-2.c
@@ -0,0 +1,488 @@
+/* Test of turning floating-point exceptions into traps (signals).
+   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 "infinity.h"
+#include "snan.h"
+#include "macros.h"
+
+/* Operations that should raise various floating-point exceptions.  */
+
+/* For a list, see the glibc documentation
+   <https://www.gnu.org/software/libc/manual/html_node/FP-Exceptions.html>  */
+
+#define infinityf my_infinityf /* Avoid collision with Cygwin's <math.h> */
+static float volatile infinityf, qnanf;
+static double volatile infinityd, qnand;
+static long double volatile infinityl, qnanl;
+
+static void
+raise_invalid_addition (char type)
+{
+  switch (type)
+    {
+    case 'f':
+      {
+        float volatile a, b;
+        _GL_UNUSED float volatile c;
+        a = infinityf; b = - infinityf;
+        c = a + b;
+      }
+      break;
+    case 'd':
+      {
+        double volatile a, b;
+        _GL_UNUSED double volatile c;
+        a = infinityd; b = - infinityd;
+        c = a + b;
+      }
+      break;
+    case 'l':
+      {
+        long double volatile a, b;
+        _GL_UNUSED long double volatile c;
+        a = infinityl; b = - infinityl;
+        c = a + b;
+      }
+      break;
+    }
+}
+
+static void
+raise_invalid_multiplication (char type)
+{
+  switch (type)
+    {
+    case 'f':
+      {
+        float volatile a, b;
+        _GL_UNUSED float volatile c;
+        a = infinityf; b = 0.0f;
+        c = a * b;
+      }
+      break;
+    case 'd':
+      {
+        double volatile a, b;
+        _GL_UNUSED double volatile c;
+        a = infinityd; b = 0.0;
+        c = a * b;
+      }
+      break;
+    case 'l':
+      {
+        long double volatile a, b;
+        _GL_UNUSED long double volatile c;
+        a = infinityl; b = 0.0L;
+        c = a * b;
+      }
+      break;
+    }
+}
+
+static void
+raise_invalid_division (char type)
+{
+  switch (type)
+    {
+    case 'f':
+      {
+        float volatile a, b;
+        _GL_UNUSED float volatile c;
+        a = infinityf; b = - infinityf;
+        c = a / b;
+      }
+      break;
+    case 'd':
+      {
+        double volatile a, b;
+        _GL_UNUSED double volatile c;
+        a = infinityd; b = - infinityd;
+        c = a / b;
+      }
+      break;
+    case 'l':
+      {
+        long double volatile a, b;
+        _GL_UNUSED long double volatile c;
+        a = infinityl; b = - infinityl;
+        c = a / b;
+      }
+      break;
+    }
+}
+
+static void
+raise_invalid_comparison (char type)
+{
+  switch (type)
+    {
+    case 'f':
+      {
+        float volatile a, b;
+        _GL_UNUSED int volatile c;
+        a = qnanf; b = qnanf;
+        c = a > b;
+      }
+      break;
+    case 'd':
+      {
+        double volatile a, b;
+        _GL_UNUSED int volatile c;
+        a = qnand; b = qnand;
+        c = a > b;
+      }
+      break;
+    case 'l':
+      {
+        long double volatile a, b;
+        _GL_UNUSED int volatile c;
+        a = qnanl; b = qnanl;
+        c = a > b;
+      }
+      break;
+    }
+}
+
+static void
+raise_invalid_snan (char type)
+{
+  switch (type)
+    {
+    case 'f':
+      {
+        float volatile a, b;
+        _GL_UNUSED float volatile c;
+        a = SNaNf (); b = 1.0f;
+        c = a + b;
+      }
+      break;
+    case 'd':
+      {
+        double volatile a, b;
+        _GL_UNUSED double volatile c;
+        a = SNaNd (); b = 1.0;
+        c = a + b;
+      }
+      break;
+    case 'l':
+      {
+        long double volatile a, b;
+        _GL_UNUSED long double volatile c;
+        a = SNaNl (); b = 1.0L;
+        c = a + b;
+      }
+      break;
+    }
+}
+
+static void
+raise_divbyzero (char type)
+{
+  switch (type)
+    {
+    case 'f':
+      {
+        float volatile a, b;
+        _GL_UNUSED float volatile c;
+        a = 2.5f; b = - 0.0f;
+        c = a / b;
+      }
+      break;
+    case 'd':
+      {
+        double volatile a, b;
+        _GL_UNUSED double volatile c;
+        a = 2.5; b = - 0.0;
+        c = a / b;
+      }
+      break;
+    case 'l':
+      {
+        long double volatile a, b;
+        _GL_UNUSED long double volatile c;
+        a = 2.5L; b = - 0.0L;
+        c = a / b;
+      }
+      break;
+    }
+}
+
+static void
+raise_overflow (char type)
+{
+  switch (type)
+    {
+    case 'f':
+      {
+        float volatile a, b;
+        _GL_UNUSED float volatile c;
+        a = 1e20f; b = 1e30f;
+        c = a * b;
+      }
+      break;
+    case 'd':
+      {
+        double volatile a, b;
+        _GL_UNUSED double volatile c;
+        a = 1e160; b = 1e260;
+        c = a * b;
+      }
+      break;
+    case 'l':
+      {
+        long double volatile a, b;
+        _GL_UNUSED long double volatile c;
+        a = 1e200L; b = 1e300L;
+        c = a * b;
+        c = c * c;
+        c = c * c;
+        c = c * c;
+        c = c * c;
+      }
+      break;
+    }
+}
+
+static void
+raise_underflow (char type)
+{
+  switch (type)
+    {
+    case 'f':
+      {
+        float volatile a, b;
+        _GL_UNUSED float volatile c;
+        a = 1e-20f; b = 1e-30f;
+        c = a * b;
+      }
+      break;
+    case 'd':
+      {
+        double volatile a, b;
+        _GL_UNUSED double volatile c;
+        a = 1e-160; b = 1e-260;
+        c = a * b;
+      }
+      break;
+    case 'l':
+      {
+        long double volatile a, b;
+        _GL_UNUSED long double volatile c;
+        a = 1e-200L; b = 1e-300L;
+        c = a * b;
+        c = c * c;
+        c = c * c;
+        c = c * c;
+        c = c * c;
+      }
+      break;
+    }
+}
+
+static void
+raise_inexact (char type)
+{
+  switch (type)
+    {
+    case 'f':
+      {
+        float volatile a, b;
+        _GL_UNUSED float volatile c;
+        a = 2.5f; b = 3.0f;
+        c = a / b;
+      }
+      break;
+    case 'd':
+      {
+        double volatile a, b;
+        _GL_UNUSED double volatile c;
+        a = 2.5; b = 3.0;
+        c = a / b;
+      }
+      break;
+    case 'l':
+      {
+        long double volatile a, b;
+        _GL_UNUSED long double volatile c;
+        a = 2.5L; b = 3.0L;
+        c = a / b;
+      }
+      break;
+    }
+}
+
+int
+main (int argc, char *argv[])
+{
+  if (argc > 3)
+    {
+      const char *operation_arg = argv[1];
+      const char *procedure_arg = argv[2];
+      const char *type_arg = argv[3];
+
+      void (*operation) (char) = NULL;
+      int expected_exceptions = 0;
+      int possible_exceptions = 0;
+
+      /* Preparations (to be executed before we call feenableexcept).  */
+      infinityf = Infinityf ();
+      qnanf = NaNf ();
+      infinityd = Infinityd ();
+      qnand = NaNd ();
+      infinityl = Infinityl ();
+      qnanl = NaNl ();
+      feclearexcept (FE_ALL_EXCEPT);
+
+      /* Analyze the operation argument.  */
+      switch (operation_arg[0])
+        {
+        case '1':
+          operation = raise_invalid_addition;
+          expected_exceptions = FE_INVALID;
+          break;
+        case '2':
+          operation = raise_invalid_multiplication;
+          expected_exceptions = FE_INVALID;
+          break;
+        case '3':
+          operation = raise_invalid_division;
+          expected_exceptions = FE_INVALID;
+          break;
+        case '4':
+          operation = raise_invalid_comparison;
+          expected_exceptions = FE_INVALID;
+          break;
+        case '5':
+          operation = raise_invalid_snan;
+          expected_exceptions = FE_INVALID;
+          break;
+        case '6':
+          operation = raise_divbyzero;
+          expected_exceptions = FE_DIVBYZERO;
+          break;
+        case '7':
+          operation = raise_overflow;
+          expected_exceptions = FE_OVERFLOW;
+          possible_exceptions = FE_INEXACT;
+          break;
+        case '8':
+          operation = raise_underflow;
+          expected_exceptions = FE_UNDERFLOW;
+          possible_exceptions = FE_INEXACT;
+          break;
+        case '9':
+          operation = raise_inexact;
+          expected_exceptions = FE_INEXACT;
+          break;
+        }
+
+      /* Analyze the procedure argument.  */
+      switch (procedure_arg[0])
+        {
+        /* These three procedures should lead to a trap.  */
+        case 'p':
+          if (feenableexcept (expected_exceptions) == -1)
+            goto skip;
+          break;
+        case 'q':
+          if (feenableexcept (FE_ALL_EXCEPT) == -1)
+            goto skip;
+          break;
+        case 'r':
+          if (feenableexcept (FE_ALL_EXCEPT) == -1)
+            goto skip;
+          ASSERT (fedisableexcept (FE_ALL_EXCEPT & ~expected_exceptions)
+                  == FE_ALL_EXCEPT);
+          break;
+        /* This procedure should *not* lead to a trap.  */
+        case 's':
+          if (feenableexcept (FE_ALL_EXCEPT & ~(expected_exceptions | possible_exceptions)) == -1)
+            goto skip;
+          break;
+        }
+
+      /* Avoid known test failures.  */
+      int known_failure = 0;
+      /* The '4' tests do not work on Linux/powerpc* and Linux/s390*, as well as
+         on GNU/kFreeBSD/i386, GNU/kFreeBSD/x86_64, musl libc/i386,
+         musl libc/powerpc64le, macOS/i386, macOS/x86_64, macOS/arm64,
+         FreeBSD/i386, FreeBSD/x86_64, NetBSD/i386, NetBSD/x86_64, OpenBSD/i386,
+         OpenBSD/x86_64, AIX/powerpc, Solaris/i386, Solaris/x86_64,
+         Cygwin/x86_64, native Windows/i386,
+         native Windows/x86_64.  */
+      #if (__GLIBC__ >= 2 && (defined __powerpc__ || (defined __s390__ || defined __s390x__))) \
+          || (__GLIBC__ >= 2 && __FreeBSD_kernel__ && ((defined __x86_64__ || defined _M_X64) || (defined __i386 || defined _M_IX86))) \
+          || (defined MUSL_LIBC && ((defined __i386 || defined _M_IX86) || defined __powerpc__)) \
+          || ((defined __APPLE__ && defined __MACH__) && ((defined __x86_64__ || defined _M_X64) || (defined __i386 || defined _M_IX86) || defined __aarch64__)) \
+          || ((defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__) && ((defined __x86_64__ || defined _M_X64) || (defined __i386 || defined _M_IX86))) \
+          || (defined _AIX && defined __powerpc__) \
+          || (defined __sun && ((defined __x86_64__ || defined _M_X64) || (defined __i386 || defined _M_IX86))) \
+          || (defined __CYGWIN__ && (defined __x86_64__ || defined _M_X64)) \
+          || (defined _WIN32 && ((defined __x86_64__ || defined _M_X64) || (defined __i386 || defined _M_IX86)))
+      known_failure |= (operation_arg[0] == '4');
+      #endif
+      /* The '7' and '8' tests, with types 'f' and 'd', do not work reliably
+         on Linux/i386.  */
+      #if defined __i386 || defined _M_IX86
+      known_failure |= (operation_arg[0] == '7' || operation_arg[0] == '8');
+      #endif
+      /* The '9' tests do not work on Linux/alpha, musl libc/powerpc64le,
+         AIX/powerpc.  */
+      #if (__GLIBC__ >= 2 && defined __alpha) \
+          || ((defined MUSL_LIBC || defined _AIX) && defined __powerpc__)
+      known_failure |= (operation_arg[0] == '9');
+      #endif
+      /* The 'l' tests do not work on Linux/loongarch64 with glibc 2.37.
+         Likewise on Linux/alpha with glibc 2.7 on Linux 2.6.26.
+         Likewise on FreeBSD 12.2/sparc.
+         Cause unknown.  */
+      #if (__GLIBC__ >= 2 && defined __loongarch__) \
+          || ((__GLIBC__ == 2 && __GLIBC_MINOR__ < 36) && defined __alpha) \
+          || (defined __FreeBSD__ && defined __sparc)
+      known_failure |= (type_arg[0] == 'l');
+      #endif
+      if (known_failure)
+        {
+          fputs ("Skipping test: known failure on this platform\n", stderr);
+          return 77;
+        }
+
+      /* Analyze the type argument.  */
+      switch (type_arg[0])
+        {
+        case 'f':
+        case 'd':
+        case 'l':
+          operation (type_arg[0]);
+          break;
+        }
+    }
+
+  return 0;
+
+ skip:
+  fputs ("Skipping test: trapping floating-point exceptions are not supported on this machine.\n", stderr);
+  return 77;
+}
diff --git a/tests/test-fenv-except-trapping-2.sh b/tests/test-fenv-except-trapping-2.sh
new file mode 100755
index 0000000000..d51197feb9
--- /dev/null
+++ b/tests/test-fenv-except-trapping-2.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+# Test of turning floating-point exceptions into traps (signals).
+
+final_rc=0
+
+for operation in 1 2 3 4 5 6 7 8 9; do
+  for procedure in p q r s; do
+    for type in f d l; do
+      ${CHECKER} ./test-fenv-except-trapping-2${EXEEXT} $operation $procedure $type
+      rc=$?
+      if test $rc = 77; then
+        if test $final_rc = 0; then
+          final_rc=77
+        fi
+      else
+        if test $procedure = s; then
+          if test $rc != 0; then
+            echo "Failed (got a trap): ./test-fenv-except-trapping-2 $operation $procedure $type" 1>&2
+            final_rc=1
+          fi
+        else
+          if test $rc = 0; then
+            echo "Failed (got no trap): ./test-fenv-except-trapping-2 $operation $procedure $type" 1>&2
+            final_rc=1
+          fi
+        fi
+      fi
+    done
+  done
+done
+
+exit $final_rc
-- 
2.34.1

Reply via email to