We now have macros for returning quiet NaNs (nan.h) and signalling NaNs
(snan.h). But who knows, do they actually work correctly?

There are two ways to verify this:
  - Do some operations with these values, and test whether the CPU's
    "invalid operation" flag has been set. For signalling NaNs it should
    be set, for quiet NaNs it should not.
    This is what I call "floating-point exception tracking".
  - Enable trapping for the "invalid operation" exception, i.e. let
    the CPU and the OS turn it into a signal. If a signal handler
    is installed, it should be invoked; if not, the program should
    crash.

Here are patches to do this:

0001) Adds module for tracking and trapping of floating-point exceptions.

      It's portable to most platforms. Worth noting:

      - RISC-V allows only tracking, not trapping, of floating-point exceptions.
        That's simply because the fcsr register has only bits for floating-point
        exception status, but no bits for trapping floating-point exceptions.

      - On i386 platforms (this includes x86_64 platforms with "gcc -m32"),
        tracking and trapping of floating-point exceptions *should* work,
        according to the Intel documentation. But it doesn't do so reliably.
        Depending on gcc optimizations, it sometimes works and sometimes not.
        When compiling without gcc optimizations, tracking and trapping of
        floating-point exceptions appears to work for 'long double' operations,
        but not for 'float' and 'double' operations.

        I observe this across different OSes and across CPU vendors (AMD and
        Intel). I tried adding sync instructions ("cpuid" or "fwait"); that
        did not help. I also tried the gcc options -mfpmath=387 and
        -mfpmath=sse; that did not help either.

        It looks like the Intel 'fld' instruction, when loading a 32-bit or
        64-bit SNaN into a register, converts it to an 80-bit QNaN without
        setting the Invalid Operation flag in the fstat register.

        Is there a way to get this working?

0002) Adds tests for nan.h.

0003) Adds tests for snan.h.

0004) Adds workarounds to avoid test failures on various platforms.


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

        nan, snan tests: Avoid test failures.
        * tests/test-nan-1.c (main): Special handling of arm CPUs with software
        floating-point emulation.
        * tests/test-snan-1.c (main): Likewise. Disable tests that are known to
        fail.
        * tests/test-snan-2.c (main): Skip tests that are known to fail.
        * modules/snan-tests (Files): Add m4/math_h.m4.
        (configure.ac): Require gl_LONG_DOUBLE_VS_DOUBLE.
        * m4/math_h.m4 (gl_LONG_DOUBLE_VS_DOUBLE): Mention also NetBSD/sparc32.

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

        snan: Add tests.
        * tests/test-snan-1.c: New file.
        * tests/test-snan-2.sh: New file.
        * tests/test-snan-2.c: New file.
        * modules/snan-tests: New file.

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

        nan: Add tests.
        * tests/test-nan-1.c: New file.
        * tests/test-nan-2.c: New file.
        * modules/nan-tests: New file.

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

        fpe-tracking, fpe-trapping: New modules.
        * lib/fpe-trapping.h: New file.
        * m4/mathfunc.m4 (gl_MATHFUNC): Handle also the type fp_except_t.
        * m4/fpe.m4: New file.
        * modules/fpe-tracking: New file.
        * modules/fpe-trapping: New file.

From a85649a3cc494396dd39be43085fcdc81d21b337 Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Fri, 13 Oct 2023 00:41:57 +0200
Subject: [PATCH 1/4] fpe-tracking, fpe-trapping: New modules.

* lib/fpe-trapping.h: New file.
* m4/mathfunc.m4 (gl_MATHFUNC): Handle also the type fp_except_t.
* m4/fpe.m4: New file.
* modules/fpe-tracking: New file.
* modules/fpe-trapping: New file.
---
 ChangeLog            |   9 +
 lib/fpe-trapping.h   | 614 +++++++++++++++++++++++++++++++++++++++++++
 m4/fpe.m4            | 115 ++++++++
 m4/mathfunc.m4       |  16 +-
 modules/fpe-tracking |  27 ++
 modules/fpe-trapping |  30 +++
 6 files changed, 804 insertions(+), 7 deletions(-)
 create mode 100644 lib/fpe-trapping.h
 create mode 100644 m4/fpe.m4
 create mode 100644 modules/fpe-tracking
 create mode 100644 modules/fpe-trapping

diff --git a/ChangeLog b/ChangeLog
index f0f23b8bbb..a3d58dc308 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2023-10-12  Bruno Haible  <br...@clisp.org>
+
+	fpe-tracking, fpe-trapping: New modules.
+	* lib/fpe-trapping.h: New file.
+	* m4/mathfunc.m4 (gl_MATHFUNC): Handle also the type fp_except_t.
+	* m4/fpe.m4: New file.
+	* modules/fpe-tracking: New file.
+	* modules/fpe-trapping: New file.
+
 2023-10-12  Bruno Haible  <br...@clisp.org>
 
 	snan: Comments.
diff --git a/lib/fpe-trapping.h b/lib/fpe-trapping.h
new file mode 100644
index 0000000000..6a63327da9
--- /dev/null
+++ b/lib/fpe-trapping.h
@@ -0,0 +1,614 @@
+/* Trapping floating-point exceptions.
+   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/>.  */
+
+/* HAVE_FPE_TRAPPING
+   Defined to 1 when sigfpe_on_invalid is available.  */
+
+/* sigfpe_on_invalid
+   Enables a SIGFPE signal when an FE_INVALID exception occurs.
+   A SIGFPE signal by default terminates the program.
+   Returns >= 0 when successful, -1 upon failure.  */
+
+
+#define HAVE_FPE_TRAPPING 1
+
+#if HAVE_FEENABLEEXCEPT
+/* glibc, FreeBSD ≥ 6.0, NetBSD ≥ 7.0, OpenBSD ≥ 5.0, Cygwin ≥ 1.7.8, Android, Haiku.  */
+
+# include <fenv.h>
+
+static int
+sigfpe_on_invalid ()
+{
+  /* Clear FE_INVALID exceptions from past operations.  */
+  feclearexcept (FE_INVALID);
+
+  /* An FE_INVALID exception shall trigger a SIGFPE signal.
+     This call may fail on arm, arm64, riscv64 CPUs.
+     Also, possibly a bug in glibc/sysdeps/m68k/fpu/feenablxcpt.c: it sets
+     only bit 13, but should better set both bit 14 and bit 13 of the
+     control register.  */
+  int ret = feenableexcept (FE_INVALID);
+  if (ret == -1)
+    return -1;
+
+  #if ((__GLIBC__ == 2 && __GLIBC_MINOR__ < 20) || defined __FreeBSD__ || defined __NetBSD__) && defined __aarch64__
+  /* Work around a bug with glibc 2.19 and FreeBSD 12.2 on arm64 CPUs:
+     feenableexcept returns success even if the CPU does not support the
+     request.  */
+  fenv_t env;
+  fegetenv (&env);
+  #if __GLIBC__ >= 2 || defined __NetBSD__
+  /* fenv_t is a struct { unsigned int __fpcr, __fpsr; }  */
+  if (((FE_INVALID << 8) & ~env.__fpcr) != 0)
+    return -1;
+  #else /* defined __FreeBSD__ */
+  /* fenv_t is an 'uint64_t' that merges fpcr and fpsr.
+     But watch out for this incompatible change:
+     <https://cgit.freebsd.org/src/commit/?id=34cc08e336987a8ebc316595e3f552a4c09f1fd4>  */
+  #if __FreeBSD__ < 14
+  if (((FE_INVALID << 8) & ~env) != 0)
+  #else
+  if (((FE_INVALID << 8) & ~(env >> 32)) != 0)
+  #endif
+    return -1;
+  #endif
+  #endif
+
+  #if (defined __FreeBSD__ || defined __NetBSD__) && defined __arm__
+  /* Work around a bug with FreeBSD 12.2 on arm64 CPUs:
+     feenableexcept returns success even if the CPU does not support the
+     request.  */
+  /* Test whether fpscr was actually changed as desired.  */
+  fenv_t env;
+  fegetenv (&env);
+  if (((FE_INVALID << 8) & ~env) != 0)
+    return -1;
+  #endif
+
+  return 0;
+}
+
+/* But it does not work on RISC-V.  That's because the fcsr register has only
+   bits for floating-point exception status, but no bits for trapping
+   floating-point exceptions.  */
+# if defined __riscv
+#  undef HAVE_FPE_TRAPPING
+# endif
+
+#elif HAVE_FPSETMASK
+/* FreeBSD ≥ 6.0, NetBSD ≥ 1.4, OpenBSD ≥ 3.1, IRIX, Solaris, Minix ≥ 3.2.  */
+
+# 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 int
+sigfpe_on_invalid ()
+{
+  fpsetmask (fpgetmask () | FP_X_INV);
+
+  return 0;
+}
+
+#elif HAVE_FESETTRAPENABLE
+/* HP-UX, QNX */
+
+# include <fenv.h>
+
+static int
+sigfpe_on_invalid ()
+{
+  fesettrapenable (fegettrapenable () | FE_INVALID);
+
+  return 0;
+}
+
+#elif defined _AIX
+/* AIX */
+
+# include <fptrap.h>
+
+static int
+sigfpe_on_invalid ()
+{
+  /* Enable precise trapping mode.
+     Documentation: <https://www.ibm.com/docs/en/aix/7.3?topic=f-fp-trap-subroutine>  */
+  fp_trap (FP_TRAP_SYNC);
+  /* 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>  */
+  fp_enable (TRP_INVALID);
+
+  return 0;
+}
+
+#elif __MINGW32__ && defined __x86_64__
+/* mingw/x86_64 */
+
+# include <fenv.h>
+
+static int
+sigfpe_on_invalid ()
+{
+  /* Clear FE_INVALID exceptions from past operations.  */
+  feclearexcept (FE_INVALID);
+
+  /* An FE_INVALID exception shall trigger a SIGFPE signal.
+     fctrl bits 5..2,0 indicate which floating-point exceptions shall, when
+     occurring in the 387 compatible floating-point unit, trigger a trap rather
+     than merely set the corresponding bit in the fstat register.
+     mxcsr bits 12..9,7 indicate which floating-point exceptions shall, when
+     occurring in the SSE registers floating-point unit, trigger a trap rather
+     than merely set the corresponding bit in the lower part of the mxcsr
+     register.  */
+  /* mingw's _controlfp_s implementation
+     <https://github.com/mingw-w64/mingw-w64/blob/master/mingw-w64-crt/secapi/_controlfp_s.c>
+     is broken.  This code here works.  */
+  {
+    unsigned short fctrl, orig_fctrl;
+    unsigned int mxcsr, orig_mxcsr;
+
+    __asm__ __volatile__ ("fstcw %0" : "=m" (*&fctrl));
+    __asm__ __volatile__ ("stmxcsr %0" : "=m" (*&mxcsr));
+    orig_fctrl = fctrl;
+    orig_mxcsr = mxcsr;
+    fctrl &= ~FE_INVALID;
+    mxcsr &= ~(FE_INVALID << 7);
+    if (!(fctrl == orig_fctrl && mxcsr == orig_mxcsr))
+      {
+        __asm__ __volatile__ ("fldcw %0" : : "m" (*&fctrl));
+        __asm__ __volatile__ ("ldmxcsr %0" : : "m" (*&mxcsr));
+      }
+  }
+
+  return 0;
+}
+
+#elif defined _WIN32 && !defined __CYGWIN__
+/* native Windows */
+
+# include <float.h>
+
+static int
+sigfpe_on_invalid ()
+{
+  /* Documentation:
+     <https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/controlfp-s>  */
+  unsigned int control;
+  if (_controlfp_s (&control, 0, 0) == 0)
+    if (_controlfp_s (&control, control & ~_EM_INVALID, _MCW_EM) == 0)
+      return 0;
+
+  return -1;
+}
+
+#elif MUSL_LIBC && defined __x86_64__
+/* musl libc/x86_64 */
+
+# include <fenv.h>
+
+static int
+sigfpe_on_invalid ()
+{
+  fenv_t env, orig_env;
+  fegetenv (&env);
+  orig_env = env;
+
+  /* Clear FE_INVALID exceptions from past operations.
+     fstat bits 5..2,0 indicate which floating-point exceptions have occurred
+     since the respective bit was last set to zero, in the 387 compatible
+     floating-point unit.
+     mxcsr bits 5..2,0 indicate which floating-point exceptions have occurred
+     since the respective bit was last set to zero, in the SSE registers
+     floating-point unit.  */
+  env.__status_word &= ~FE_INVALID;
+  env.__mxcsr &= ~FE_INVALID;
+  /* An FE_INVALID exception shall trigger a SIGFPE signal.
+     fctrl bits 5..2,0 indicate which floating-point exceptions shall, when
+     occurring in the 387 compatible floating-point unit, trigger a trap rather
+     than merely set the corresponding bit in the fstat register.
+     mxcsr bits 12..9,7 indicate which floating-point exceptions shall, when
+     occurring in the SSE registers floating-point unit, trigger a trap rather
+     than merely set the corresponding bit in the lower part of the mxcsr
+     register.  */
+  env.__control_word &= ~FE_INVALID;
+  env.__mxcsr &= ~(FE_INVALID << 7);
+
+  if (!(env.__control_word == orig_env.__control_word
+        && env.__status_word == orig_env.__status_word
+        && env.__mxcsr == orig_env.__mxcsr))
+    fesetenv (&env);
+
+  return 0;
+}
+
+#elif MUSL_LIBC && defined __i386__
+/* musl libc/x86 */
+
+# include <fenv.h>
+
+static int
+sigfpe_on_invalid ()
+{
+  fenv_t env, orig_env;
+  fegetenv (&env);
+  orig_env = env;
+
+  /* The mxcsr register exists, like for x86_64, but only if the CPU supports
+     SSE instructions.  Here it is not part of fenv_t.  Instead, it is
+     transparently handled by the fegetenv and fesetenv functions.  */
+
+  /* Clear FE_INVALID exceptions from past operations.
+     fstat bits 5..2,0 indicate which floating-point exceptions have occurred
+     since the respective bit was last set to zero, in the 387 compatible
+     floating-point unit.  */
+  env.__status_word &= ~FE_INVALID;
+  /* An FE_INVALID exception shall trigger a SIGFPE signal.
+     fctrl bits 5..2,0 indicate which floating-point exceptions shall, when
+     occurring in the 387 compatible floating-point unit, trigger a trap rather
+     than merely set the corresponding bit in the fstat register.  */
+  env.__control_word &= ~FE_INVALID;
+
+  if (!(env.__control_word == orig_env.__control_word
+        && env.__status_word == orig_env.__status_word))
+    fesetenv (&env);
+
+  return 0;
+}
+
+#elif MUSL_LIBC && defined __aarch64__
+/* musl libc/arm64 */
+
+# include <fenv.h>
+
+static int
+sigfpe_on_invalid ()
+{
+  fenv_t env, orig_env;
+  fegetenv (&env);
+  orig_env = env;
+
+  /* Clear FE_INVALID exceptions from past operations.
+     fpsr bits 4..0 indicate which floating-point exceptions have occurred
+     since the respective bit was last set to zero.  */
+  env.__fpsr &= ~FE_INVALID;
+  /* An FE_INVALID exception shall trigger a SIGFPE signal.
+     fpcr bits 12..8 indicate which floating-point exceptions shall, when
+     occurring, trigger a trap rather than merely set the corresponding bit
+     in the fpsr register.  */
+  env.__fpcr |= (FE_INVALID << 8);
+
+  if (!(env.__fpsr == orig_env.__fpsr && env.__fpcr == orig_env.__fpcr))
+    {
+      fesetenv (&env);
+      /* Test whether __fpcr was actually changed as desired.  */
+      fenv_t new_env;
+      fegetenv (&new_env);
+      if (new_env.__fpcr != env.__fpcr)
+        return -1;
+    }
+
+  return 0;
+}
+
+#elif MUSL_LIBC && defined __arm__
+/* musl libc/arm */
+
+# include <fenv.h>
+
+static int
+sigfpe_on_invalid ()
+{
+  fenv_t env;
+  unsigned int orig_fpscr;
+  fegetenv (&env);
+  orig_fpscr = env.__cw;
+
+  /* Clear FE_INVALID exceptions from past operations.
+     fpscr bits 4..0 indicate which floating-point exceptions have occurred
+     since the respective bit was last set to zero.  */
+  env.__cw &= ~FE_INVALID;
+  /* An FE_INVALID exception shall trigger a SIGFPE signal.
+     fpscr bits 12..8 indicate which floating-point exceptions shall, when
+     occurring, trigger a trap rather than merely set the corresponding bit
+     in the fpscr register.  */
+  env.__cw |= (FE_INVALID << 8);
+
+  if (!(env.__cw == orig_fpscr))
+    {
+      fesetenv (&env);
+      /* Test whether fpscr was actually changed as desired.  */
+      fenv_t new_env;
+      fegetenv (&new_env);
+      if (new_env.__cw != env.__cw)
+        return -1;
+    }
+
+  return 0;
+}
+
+#elif MUSL_LIBC && defined __m68k__
+/* musl libc/m68k */
+
+# include <fenv.h>
+
+static int
+sigfpe_on_invalid ()
+{
+  fenv_t env, orig_env;
+  fegetenv (&env);
+  orig_env = env;
+
+  /* Clear FE_INVALID exceptions from past operations.
+     fpsr bits 7..3 indicate which floating-point exceptions have occurred
+     since the respective bit was last set to zero.  */
+  env.__status_register &= ~FE_INVALID;
+  /* An FE_INVALID exception shall trigger a SIGFPE signal.
+     fpcr bits 15..8 indicate which floating-point exceptions shall, when
+     occurring, trigger a trap rather than merely set the corresponding bit
+     in the fpsr register:
+       - bit 15: branch/set on unordered
+       - bit 14: signaling not-a-number
+       - bit 13: operand error
+       - bit 12: overflow
+       - bit 11: underflow
+       - bit 10: divide by zero
+       - bit 9:  inexact operation
+       - bit 8:  inexact decimal input
+     Although (FE_INVALID << 6) has bit 13 set, we need to set bit 14 and
+     bit 13.  */
+  env.__control_register |= (1U << 14) | (FE_INVALID << 6);
+
+  if (!(env.__status_register == orig_env.__status_register
+        && env.__control_register == orig_env.__control_register))
+    fesetenv (&env);
+
+  return 0;
+}
+
+#elif MUSL_LIBC && defined __mips__
+/* musl libc/mips64,mipsn32,mips32 */
+
+# include <fenv.h>
+
+static int
+sigfpe_on_invalid ()
+{
+  fenv_t env, orig_env;
+  fegetenv (&env);
+  orig_env = env;
+
+  /* Clear FE_INVALID exceptions from past operations.
+     fcsr bits 6..2 indicate which floating-point exceptions have occurred
+     since the respective bit was last set to zero.
+     fcsr bits 17..12 indicate which floating-point exceptions have occurred
+     in the most recent instruction.  */
+  env.__cw &= ~((FE_INVALID << 10) | FE_INVALID);
+  /* An FE_INVALID exception shall trigger a SIGFPE signal.
+     fcsr bits 11..7 indicate which floating-point exceptions shall, when
+     occurring, trigger a trap rather than merely set the corresponding bit
+     in the fcsr register.  */
+  env.__cw |= (FE_INVALID << 5);
+
+  if (!(env.__cw == orig_env.__cw))
+    fesetenv (&env);
+
+  return 0;
+}
+
+#elif MUSL_LIBC && (defined __powerpc__ || defined __powerpc64__)
+/* musl libc/powerpc64,powerpc */
+
+# include <fenv.h>
+# include <sys/prctl.h>
+
+static int
+sigfpe_on_invalid ()
+{
+  /* fenv_t is a 'double'.  */
+  union { unsigned long long u; fenv_t f; } env, orig_env;
+  fegetenv (&env.f);
+  orig_env = env;
+
+  /* Clear FE_INVALID exceptions from past operations.
+     fpscr bits 28..25 indicate which floating-point exceptions, other than
+     FE_INVALID, have occurred since the respective bit was last set to zero.
+     fpscr bits 24..19, 10..8 do the same thing, for various kinds of Invalid
+     Operation.  fpscr bit 29 is the summary (the OR) of all these bits.
+     Instead of clearing FE_INVALID (= bit 29), we need to clear these
+     individual bits.  */
+  env.u &= ~FE_ALL_INVALID; /* not ~FE_INVALID ! */
+  /* An FE_INVALID exception shall trigger a SIGFPE signal.
+     fpscr bits 7..3 indicate which floating-point exceptions shall, when
+     occurring, trigger a trap rather than merely set the corresponding bit
+     in the fpscr register.  */
+  env.u |= (FE_INVALID >> 22);
+
+  if (!(env.u == orig_env.u))
+    {
+      /* Enable precise trapping mode.  */
+      prctl (PR_SET_FPEXC, PR_FP_EXC_PRECISE);
+
+      fesetenv (&env.f);
+    }
+
+  return 0;
+}
+
+#elif MUSL_LIBC && (defined __s390__ || defined __s390x__)
+/* musl libc/s390x,s390 */
+
+# include <fenv.h>
+
+static int
+sigfpe_on_invalid ()
+{
+  fenv_t env, orig_env;
+  fegetenv (&env);
+  orig_env = env;
+
+  /* Clear FE_INVALID exceptions from past operations.
+     fpc bits 23..19 indicate which floating-point exceptions have occurred
+     since the respective bit was last set to zero.
+     fpc bits 15..11 are part of the "data exception code" (DXC) and have a
+     similar meaning if bits 9..8 are both zero.  */
+  env &= ~FE_INVALID;
+  if ((env & 0x00000300) == 0)
+    env &= ~(FE_INVALID >> 8);
+  /* An FE_INVALID exception shall trigger a SIGFPE signal.
+     fpc bits 31..27 indicate which floating-point exceptions shall, when
+     occurring, trigger a trap rather than merely set the corresponding bit
+     in the fpc register.  */
+  env |= ((unsigned int) FE_INVALID << 8);
+
+  if (!(env == orig_env))
+    fesetenv (&env);
+
+  return 0;
+}
+
+#elif MUSL_LIBC && defined __sh__
+/* musl libc/sh4 */
+
+# include <fenv.h>
+
+static int
+sigfpe_on_invalid ()
+{
+  fenv_t env, orig_env;
+  fegetenv (&env);
+  orig_env = env;
+
+  /* Clear FE_INVALID exceptions from past operations.
+     fpscr bits 6..2 indicate which floating-point exceptions have occurred
+     since the respective bit was last set to zero.  */
+  env.__cw &= ~FE_INVALID;
+  /* An FE_INVALID exception shall trigger a SIGFPE signal.
+     fpscr bits 11..7 indicate which floating-point exceptions shall, when
+     occurring, trigger a trap rather than merely set the corresponding bit
+     in the fpscr register.  */
+  env.__cw |= (FE_INVALID << 5);
+
+  if (!(env.__cw == orig_env.__cw))
+    fesetenv (&env);
+
+  return 0;
+}
+
+#elif (defined __APPLE__ && defined __MACH__) && (defined __i386__ || defined __x86_64__)
+/* Mac OS X/i386,x86_64 */
+
+# include <fenv.h>
+
+static int
+sigfpe_on_invalid ()
+{
+  fenv_t env, orig_env;
+  fegetenv (&env);
+  orig_env = env;
+
+  /* Clear FE_INVALID exceptions from past operations.
+     fstat bits 5..2,0 indicate which floating-point exceptions have occurred
+     since the respective bit was last set to zero, in the 387 compatible
+     floating-point unit.
+     mxcsr bits 5..2,0 indicate which floating-point exceptions have occurred
+     since the respective bit was last set to zero, in the SSE registers
+     floating-point unit.  */
+  env.__status &= ~FE_INVALID;
+  env.__mxcsr &= ~FE_INVALID;
+  /* An FE_INVALID exception shall trigger a SIGFPE signal.
+     fctrl bits 5..2,0 indicate which floating-point exceptions shall, when
+     occurring in the 387 compatible floating-point unit, trigger a trap rather
+     than merely set the corresponding bit in the fstat register.
+     mxcsr bits 12..9,7 indicate which floating-point exceptions shall, when
+     occurring in the SSE registers floating-point unit, trigger a trap rather
+     than merely set the corresponding bit in the lower part of the mxcsr
+     register.  */
+  env.__control &= ~FE_INVALID;
+  env.__mxcsr &= ~(FE_INVALID << 7);
+
+  if (!(env.__control == orig_env.__control
+        && env.__status == orig_env.__status
+        && env.__mxcsr == orig_env.__mxcsr))
+    fesetenv (&env);
+
+  return 0;
+}
+
+#elif (defined __APPLE__ && defined __MACH__) && defined __aarch64__
+/* macOS/arm64 */
+
+# include <fenv.h>
+
+static int
+sigfpe_on_invalid ()
+{
+  fenv_t env, orig_env;
+  fegetenv (&env);
+  orig_env = env;
+
+  /* Clear FE_INVALID exceptions from past operations.
+     fpsr bits 4..0 indicate which floating-point exceptions have occurred
+     since the respective bit was last set to zero.  */
+  env.__fpsr &= ~FE_INVALID;
+  /* An FE_INVALID exception shall trigger a SIGILL signal.
+     fpcr bits 12..8 indicate which floating-point exceptions shall, when
+     occurring, trigger a trap rather than merely set the corresponding bit
+     in the fpsr register.  */
+  env.__fpcr |= (FE_INVALID << 8); /* __fpcr_trap_invalid */
+
+  if (!(env.__fpsr == orig_env.__fpsr && env.__fpcr == orig_env.__fpcr))
+    fesetenv (&env);
+
+  return 0;
+}
+
+#elif (defined __APPLE__ && defined __MACH__) && defined __arm__
+/* macOS/arm */
+
+# include <fenv.h>
+
+static int
+sigfpe_on_invalid ()
+{
+  fenv_t env;
+  unsigned int orig_fpscr;
+  fegetenv (&env);
+  orig_fpscr = env.__fpscr;
+
+  /* Clear FE_INVALID exceptions from past operations.
+     fpscr bits 4..0 indicate which floating-point exceptions have occurred
+     since the respective bit was last set to zero.  */
+  env.__fpscr &= ~FE_INVALID;
+  /* An FE_INVALID exception shall trigger a signal.
+     fpscr bits 12..8 indicate which floating-point exceptions shall, when
+     occurring, trigger a trap rather than merely set the corresponding bit
+     in the fpscr register.  */
+  env.__fpscr |= (FE_INVALID << 8); /* __fpscr_trap_invalid */
+
+  if (!(env.__fpscr == orig_fpscr))
+    fesetenv (&env);
+
+  return 0;
+}
+
+#else
+
+# undef HAVE_FPE_TRAPPING
+
+#endif
diff --git a/m4/fpe.m4 b/m4/fpe.m4
new file mode 100644
index 0000000000..f8a81030a6
--- /dev/null
+++ b/m4/fpe.m4
@@ -0,0 +1,115 @@
+# fpe.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.
+
+# Prerequisites for tracking FE_INVALID exceptions.
+AC_DEFUN_ONCE([gl_FPE_TRACKING],
+[
+  AC_CACHE_CHECK([whether FE_INVALID exceptions can be tracked],
+    [gl_cv_fenv_invalid_api],
+    [AC_COMPILE_IFELSE(
+       [AC_LANG_PROGRAM(
+          [[#include <fenv.h>
+          ]],
+          [[feclearexcept (FE_INVALID);
+          ]])
+       ],
+       [gl_cv_fenv_invalid_api=yes],
+       [gl_cv_fenv_invalid_api=no])
+    ])
+  if test $gl_cv_fenv_invalid_api = yes; then
+    AC_DEFINE([HAVE_FE_INVALID], [1],
+      [Define if FE_INVALID exceptions can be programmatically tracked.])
+    AC_CACHE_CHECK([whether feclearexcept can be used without linking with libm],
+      [gl_cv_func_feclearexcept_no_libm],
+      [AC_LINK_IFELSE(
+         [AC_LANG_PROGRAM(
+            [[#include <fenv.h>
+            ]],
+            [[feclearexcept (FE_INVALID);
+            ]])
+         ],
+         [gl_cv_func_feclearexcept_no_libm=yes],
+         [gl_cv_func_feclearexcept_no_libm=no])
+      ])
+    if test $gl_cv_func_feclearexcept_no_libm != yes; then
+      FPE_TRACKING_LIBM=-lm
+    else
+      FPE_TRACKING_LIBM=
+    fi
+  else
+    FPE_TRACKING_LIBM=
+  fi
+  AC_SUBST([FPE_TRACKING_LIBM])
+])
+
+# Prerequisites for turning FE_INVALID exceptions into a SIGFPE signal.
+AC_DEFUN_ONCE([gl_FPE_TRAPPING],
+[
+  dnl Persuade glibc <fenv.h> to declare feenableexcept().
+  AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
+
+  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
+    AC_DEFINE([HAVE_FEENABLEEXCEPT], [1],
+      [Define to 1 if you have the 'feenableexcept' function.])
+  fi
+
+  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
+
+  gl_MUSL_LIBC
+
+  if test $gl_cv_func_feenableexcept_no_libm = yes \
+     || test $gl_cv_func_feenableexcept_in_libm = yes; then
+    if test $gl_cv_func_feenableexcept_no_libm != yes; then
+      FPE_TRAPPING_LIBM=-lm
+    else
+      FPE_TRAPPING_LIBM=
+    fi
+  else
+    if test $gl_cv_func_fpsetmask_no_libm = yes \
+       || test $gl_cv_func_fpsetmask_in_libm = yes; then
+      if test $gl_cv_func_fpsetmask_no_libm != yes; then
+        FPE_TRAPPING_LIBM=-lm
+      else
+        FPE_TRAPPING_LIBM=
+      fi
+    else
+      if test $gl_cv_func_fesettrapenable_no_libm = yes \
+         || test $gl_cv_func_fesettrapenable_in_libm = yes; then
+        if test $gl_cv_func_fesettrapenable_no_libm != yes; then
+          FPE_TRAPPING_LIBM=-lm
+        else
+          FPE_TRAPPING_LIBM=
+        fi
+      else
+        FPE_TRAPPING_LIBM=
+      fi
+    fi
+  fi
+  AC_REQUIRE([gl_FPE_TRACKING])
+  FPE_TRAPPING_LIBM="$FPE_TRAPPING_LIBM $FPE_TRACKING_LIBM"
+  AC_SUBST([FPE_TRAPPING_LIBM])
+])
diff --git a/m4/mathfunc.m4 b/m4/mathfunc.m4
index 00216ceae6..5146e580c0 100644
--- a/m4/mathfunc.m4
+++ b/m4/mathfunc.m4
@@ -1,4 +1,4 @@
-# mathfunc.m4 serial 13
+# mathfunc.m4 serial 14
 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,
@@ -39,12 +39,14 @@ AC_DEFUN([gl_MATHFUNC]
                                        [m4_bpatsubst(
                                           [m4_bpatsubst(
                                              [m4_bpatsubst(
-                                                [$3],
-                                                [int\( const\)? \*],
-                                                [&i_ret])],
-                                             [float\( const\)? \*], [&f_ret])],
-                                          [double\( const\)? \*], [&d_ret])],
-                                       [long double\( const\)? \*], [&l_ret])],
+                                                [m4_bpatsubst(
+                                                   [$3],
+                                                   [int\( const\)? \*],
+                                                   [&i_ret])],
+                                                [float\( const\)? \*], [&f_ret])],
+                                             [double\( const\)? \*], [&d_ret])],
+                                          [long double\( const\)? \*], [&l_ret])],
+                                       [fp_except_t], [1])],
                                     [int], [2])],
                                  [float], [1.618034f])],
                               [long double], [1.618033988749894848L])],
diff --git a/modules/fpe-tracking b/modules/fpe-tracking
new file mode 100644
index 0000000000..d1afcf94f0
--- /dev/null
+++ b/modules/fpe-tracking
@@ -0,0 +1,27 @@
+Description:
+Tracking floating-point exceptions, i.e. determining whether
+a floating-point exception has occurred during a certain computation.
+
+Files:
+m4/fpe.m4
+
+Depends-on:
+
+configure.ac:
+gl_FPE_TRACKING
+
+Makefile.am:
+
+Include:
+#if HAVE_FE_INVALID
+# include <fenv.h>
+#endif
+
+Link:
+$(FPE_TRACKING_LIBM)
+
+License:
+GPL
+
+Maintainer:
+all
diff --git a/modules/fpe-trapping b/modules/fpe-trapping
new file mode 100644
index 0000000000..d8acbe8a27
--- /dev/null
+++ b/modules/fpe-trapping
@@ -0,0 +1,30 @@
+Description:
+Trapping floating-point exceptions, i.e. turning a floating-point exception
+into a signal.
+
+Files:
+lib/fpe-trapping.h
+m4/fpe.m4
+m4/mathfunc.m4
+m4/musl.m4
+
+Depends-on:
+extensions
+
+configure.ac:
+gl_FPE_TRAPPING
+
+Makefile.am:
+lib_SOURCES += fpe-trapping.h
+
+Include:
+"fpe-trapping.h"
+
+Link:
+$(FPE_TRAPPING_LIBM)
+
+License:
+GPL
+
+Maintainer:
+all
-- 
2.34.1

>From 3c26f061e287a01a7001a03999765887ae4d5dcf Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Fri, 13 Oct 2023 00:52:03 +0200
Subject: [PATCH 2/4] nan: Add tests.

* tests/test-nan-1.c: New file.
* tests/test-nan-2.c: New file.
* modules/nan-tests: New file.
---
 ChangeLog          |  7 ++++
 modules/nan-tests  | 16 +++++++++
 tests/test-nan-1.c | 82 ++++++++++++++++++++++++++++++++++++++++++++
 tests/test-nan-2.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 189 insertions(+)
 create mode 100644 modules/nan-tests
 create mode 100644 tests/test-nan-1.c
 create mode 100644 tests/test-nan-2.c

diff --git a/ChangeLog b/ChangeLog
index a3d58dc308..117ea7d70e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2023-10-12  Bruno Haible  <br...@clisp.org>
+
+	nan: Add tests.
+	* tests/test-nan-1.c: New file.
+	* tests/test-nan-2.c: New file.
+	* modules/nan-tests: New file.
+
 2023-10-12  Bruno Haible  <br...@clisp.org>
 
 	fpe-tracking, fpe-trapping: New modules.
diff --git a/modules/nan-tests b/modules/nan-tests
new file mode 100644
index 0000000000..607e35f63b
--- /dev/null
+++ b/modules/nan-tests
@@ -0,0 +1,16 @@
+Files:
+tests/test-nan-1.c
+tests/test-nan-2.c
+tests/macros.h
+
+Depends-on:
+fpe-tracking
+fpe-trapping
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-nan-1 test-nan-2
+check_PROGRAMS += test-nan-1 test-nan-2
+test_nan_1_LDADD = $(LDADD) @FPE_TRACKING_LIBM@
+test_nan_2_LDADD = $(LDADD) @FPE_TRAPPING_LIBM@
diff --git a/tests/test-nan-1.c b/tests/test-nan-1.c
new file mode 100644
index 0000000000..9bce3225c1
--- /dev/null
+++ b/tests/test-nan-1.c
@@ -0,0 +1,82 @@
+/* Tests of quiet not-a-number.
+   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 "nan.h"
+
+#if HAVE_FE_INVALID
+
+# include <fenv.h>
+
+# include "macros.h"
+
+float volatile resultf;
+double volatile resultd;
+long double volatile resultl;
+
+int
+main ()
+{
+  /* Fetch the NaN values before we start watching out for FE_INVALID
+     exceptions, because the division 0.0 / 0.0 itself also raises an
+     FE_INVALID exception.
+     The use of 'volatile' prevents the compiler from doing constant-folding
+     optimizations on these values.  An alternative, for GCC only, would be
+     the command-line option '-fsignaling-nans'.  */
+  float volatile nanf = NaNf ();
+  double volatile nand = NaNd ();
+  long double volatile nanl = NaNl ();
+
+  /* Check that the values are really quiet.  */
+  {
+    feclearexcept (FE_INVALID);
+    resultf = nanf + 42.0f;
+    ASSERT (!fetestexcept (FE_INVALID));
+  }
+  {
+    feclearexcept (FE_INVALID);
+    resultd = nand + 42.0;
+    ASSERT (!fetestexcept (FE_INVALID));
+  }
+  {
+    feclearexcept (FE_INVALID);
+    resultl = nanl + 42.0L;
+    ASSERT (!fetestexcept (FE_INVALID));
+  }
+
+  return 0;
+}
+
+#else
+
+/* No <fenv.h> available.
+   We could use the various alternative approaches from
+   libgfortran/config/fpu-*.h, but that's not worth it.  */
+
+#include <stdio.h>
+
+int
+main ()
+{
+  fputs ("Skipping test: feclearexcept, fetestexcept, FE_INVALID not available\n", stderr);
+  return 77;
+}
+
+#endif
diff --git a/tests/test-nan-2.c b/tests/test-nan-2.c
new file mode 100644
index 0000000000..b82ebabf4b
--- /dev/null
+++ b/tests/test-nan-2.c
@@ -0,0 +1,84 @@
+/* Tests of quiet not-a-number.
+   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 "nan.h"
+
+#include <stdio.h>
+
+#include "fpe-trapping.h"
+
+#if HAVE_FPE_TRAPPING
+
+# include <fenv.h>
+
+# include "macros.h"
+
+float volatile resultf;
+double volatile resultd;
+long double volatile resultl;
+
+int
+main ()
+{
+  /* Fetch the NaN values before we start watching out for FE_INVALID
+     exceptions, because the division 0.0 / 0.0 itself also raises an
+     FE_INVALID exception.
+     The use of 'volatile' prevents the compiler from doing constant-folding
+     optimizations on these values.  An alternative, for GCC only, would be
+     the command-line option '-fsignaling-nans'.  */
+  float volatile nanf = NaNf ();
+  double volatile nand = NaNd ();
+  long double volatile nanl = NaNl ();
+
+  /* Check that the values are really quiet.  */
+
+  /* Clear FE_INVALID exceptions from past operations.  */
+  feclearexcept (FE_INVALID);
+
+  /* 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;
+    }
+
+  resultf = nanf + 42.0f;
+  resultd = nand + 42.0;
+  resultl = nanl + 42.0L;
+
+  return 0;
+}
+
+#else
+
+/* No HAVE_FPE_TRAPPING available.
+   We could use the various alternative approaches from
+   libgfortran/config/fpu-*.h, but that's not worth it.  */
+
+int
+main ()
+{
+  fputs ("Skipping test: feenableexcept or fpsetmask or fp_enable not available\n", stderr);
+  return 77;
+}
+
+#endif
-- 
2.34.1

>From 43799ecbe6dc5c5b8b254f0b86b08b810dae3573 Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Fri, 13 Oct 2023 00:59:27 +0200
Subject: [PATCH 3/4] snan: Add tests.

* tests/test-snan-1.c: New file.
* tests/test-snan-2.sh: New file.
* tests/test-snan-2.c: New file.
* modules/snan-tests: New file.
---
 ChangeLog            |  8 ++++
 modules/snan-tests   | 17 ++++++++
 tests/test-snan-1.c  | 82 +++++++++++++++++++++++++++++++++++++
 tests/test-snan-2.c  | 96 ++++++++++++++++++++++++++++++++++++++++++++
 tests/test-snan-2.sh | 40 ++++++++++++++++++
 5 files changed, 243 insertions(+)
 create mode 100644 modules/snan-tests
 create mode 100644 tests/test-snan-1.c
 create mode 100644 tests/test-snan-2.c
 create mode 100755 tests/test-snan-2.sh

diff --git a/ChangeLog b/ChangeLog
index 117ea7d70e..2a2ed122e1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2023-10-12  Bruno Haible  <br...@clisp.org>
+
+	snan: Add tests.
+	* tests/test-snan-1.c: New file.
+	* tests/test-snan-2.sh: New file.
+	* tests/test-snan-2.c: New file.
+	* modules/snan-tests: New file.
+
 2023-10-12  Bruno Haible  <br...@clisp.org>
 
 	nan: Add tests.
diff --git a/modules/snan-tests b/modules/snan-tests
new file mode 100644
index 0000000000..d3f2b8615e
--- /dev/null
+++ b/modules/snan-tests
@@ -0,0 +1,17 @@
+Files:
+tests/test-snan-1.c
+tests/test-snan-2.sh
+tests/test-snan-2.c
+tests/macros.h
+
+Depends-on:
+fpe-tracking
+fpe-trapping
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-snan-1 test-snan-2.sh
+check_PROGRAMS += test-snan-1 test-snan-2
+test_snan_1_LDADD = $(LDADD) @FPE_TRACKING_LIBM@
+test_snan_2_LDADD = $(LDADD) @FPE_TRAPPING_LIBM@
diff --git a/tests/test-snan-1.c b/tests/test-snan-1.c
new file mode 100644
index 0000000000..de87372f17
--- /dev/null
+++ b/tests/test-snan-1.c
@@ -0,0 +1,82 @@
+/* Tests of signalling not-a-number.
+   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 "snan.h"
+
+#if HAVE_FE_INVALID
+
+# include <fenv.h>
+
+# include "macros.h"
+
+float volatile resultf;
+double volatile resultd;
+long double volatile resultl;
+
+int
+main ()
+{
+  /* Fetch the NaN values before we start watching out for FE_INVALID
+     exceptions, because the division 0.0 / 0.0 itself also raises an
+     FE_INVALID exception.
+     The use of 'volatile' prevents the compiler from doing constant-folding
+     optimizations on these values.  An alternative, for GCC only, would be
+     the command-line option '-fsignaling-nans'.  */
+  float volatile nanf = SNaNf ();
+  double volatile nand = SNaNd ();
+  long double volatile nanl = SNaNl ();
+
+  /* Check that the values are really signalling.  */
+  {
+    feclearexcept (FE_INVALID);
+    resultf = nanf + 42.0f;
+    ASSERT (fetestexcept (FE_INVALID));
+  }
+  {
+    feclearexcept (FE_INVALID);
+    resultd = nand + 42.0;
+    ASSERT (fetestexcept (FE_INVALID));
+  }
+  {
+    feclearexcept (FE_INVALID);
+    resultl = nanl + 42.0L;
+    ASSERT (fetestexcept (FE_INVALID));
+  }
+
+  return 0;
+}
+
+#else
+
+/* No <fenv.h> available.
+   We could use the various alternative approaches from
+   libgfortran/config/fpu-*.h, but that's not worth it.  */
+
+#include <stdio.h>
+
+int
+main ()
+{
+  fputs ("Skipping test: feclearexcept, fetestexcept, FE_INVALID not available\n", stderr);
+  return 77;
+}
+
+#endif
diff --git a/tests/test-snan-2.c b/tests/test-snan-2.c
new file mode 100644
index 0000000000..e1ee7c8928
--- /dev/null
+++ b/tests/test-snan-2.c
@@ -0,0 +1,96 @@
+/* Tests of signalling not-a-number.
+   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 "snan.h"
+
+#include <stdio.h>
+
+#include "fpe-trapping.h"
+
+#if HAVE_FPE_TRAPPING
+
+# include <fenv.h>
+
+# include "macros.h"
+
+float volatile resultf;
+double volatile resultd;
+long double volatile resultl;
+
+int
+main (int argc, char *argv[])
+{
+  /* Fetch the NaN values before we start watching out for FE_INVALID
+     exceptions, because the division 0.0 / 0.0 itself also raises an
+     FE_INVALID exception.
+     The use of 'volatile' prevents the compiler from doing constant-folding
+     optimizations on these values.  An alternative, for GCC only, would be
+     the command-line option '-fsignaling-nans'.  */
+  float volatile nanf = SNaNf ();
+  double volatile nand = SNaNd ();
+  long double volatile nanl = SNaNl ();
+
+  /* Check that the values are really signalling.  */
+
+  /* Clear FE_INVALID exceptions from past operations.  */
+  feclearexcept (FE_INVALID);
+
+  /* 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;
+    }
+
+  if (argc > 1)
+    switch (argv[1][0])
+      {
+      case 'f':
+        resultf = nanf + 42.0f;
+        break;
+      case 'd':
+        resultd = nand + 42.0;
+        break;
+      case 'l':
+        resultl = nanl + 42.0L;
+        break;
+      default:
+        break;
+      }
+
+  return 0;
+}
+
+#else
+
+/* No HAVE_FPE_TRAPPING available.
+   We could use the various alternative approaches from
+   libgfortran/config/fpu-*.h, but that's not worth it.  */
+
+int
+main ()
+{
+  fputs ("Skipping test: feenableexcept or fpsetmask or fp_enable not available\n", stderr);
+  return 77;
+}
+
+#endif
diff --git a/tests/test-snan-2.sh b/tests/test-snan-2.sh
new file mode 100755
index 0000000000..596e3ea483
--- /dev/null
+++ b/tests/test-snan-2.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+# Test of signalling not-a-number.
+
+final_rc=0
+
+${CHECKER} ./test-snan-2${EXEEXT} f
+rc=$?
+if test $rc = 77; then
+  final_rc=77
+else
+  if test $rc = 0; then
+    echo "Use of SNaNf() did not signal." 1>&2
+    exit 1
+  fi
+fi
+
+${CHECKER} ./test-snan-2${EXEEXT} d
+rc=$?
+if test $rc = 77; then
+  final_rc=77
+else
+  if test $rc = 0; then
+    echo "Use of SNaNd() did not signal." 1>&2
+    exit 1
+  fi
+fi
+
+${CHECKER} ./test-snan-2${EXEEXT} l
+rc=$?
+if test $rc = 77; then
+  final_rc=77
+else
+  if test $rc = 0; then
+    echo "Use of SNaNl() did not signal." 1>&2
+    exit 1
+  fi
+fi
+
+exit $final_rc
-- 
2.34.1

>From e5118e049ba258e3ba29a58c3520ba415d3dc555 Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Fri, 13 Oct 2023 01:04:27 +0200
Subject: [PATCH 4/4] nan, snan tests: Avoid test failures.

* tests/test-nan-1.c (main): Special handling of arm CPUs with software
floating-point emulation.
* tests/test-snan-1.c (main): Likewise. Disable tests that are known to
fail.
* tests/test-snan-2.c (main): Skip tests that are known to fail.
* modules/snan-tests (Files): Add m4/math_h.m4.
(configure.ac): Require gl_LONG_DOUBLE_VS_DOUBLE.
* m4/math_h.m4 (gl_LONG_DOUBLE_VS_DOUBLE): Mention also NetBSD/sparc32.
---
 ChangeLog           | 12 +++++++++
 m4/math_h.m4        |  2 +-
 modules/snan-tests  |  2 ++
 tests/test-nan-1.c  | 24 +++++++++++++++---
 tests/test-snan-1.c | 51 +++++++++++++++++++++++++++++++++++---
 tests/test-snan-2.c | 60 +++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 142 insertions(+), 9 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 2a2ed122e1..7112234f8f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+2023-10-12  Bruno Haible  <br...@clisp.org>
+
+	nan, snan tests: Avoid test failures.
+	* tests/test-nan-1.c (main): Special handling of arm CPUs with software
+	floating-point emulation.
+	* tests/test-snan-1.c (main): Likewise. Disable tests that are known to
+	fail.
+	* tests/test-snan-2.c (main): Skip tests that are known to fail.
+	* modules/snan-tests (Files): Add m4/math_h.m4.
+	(configure.ac): Require gl_LONG_DOUBLE_VS_DOUBLE.
+	* m4/math_h.m4 (gl_LONG_DOUBLE_VS_DOUBLE): Mention also NetBSD/sparc32.
+
 2023-10-12  Bruno Haible  <br...@clisp.org>
 
 	snan: Add tests.
diff --git a/m4/math_h.m4 b/m4/math_h.m4
index 008dd6fa3b..959006472a 100644
--- a/m4/math_h.m4
+++ b/m4/math_h.m4
@@ -375,7 +375,7 @@ AC_DEFUN([gl_MATH_H_DEFAULTS]
 # Sets variable HAVE_SAME_LONG_DOUBLE_AS_DOUBLE to 0 or 1, and defines
 # HAVE_SAME_LONG_DOUBLE_AS_DOUBLE accordingly.
 # The currently known platforms where this is the case are:
-# Linux/HPPA, Minix 3.1.8, AIX 5, AIX 6 and 7 with xlc, MSVC 9.
+# Linux/HPPA, NetBSD/sparc32, Minix 3.1.8, AIX 5, AIX 6 and 7 with xlc, MSVC 9.
 AC_DEFUN([gl_LONG_DOUBLE_VS_DOUBLE],
 [
   AC_CACHE_CHECK([whether long double and double are the same],
diff --git a/modules/snan-tests b/modules/snan-tests
index d3f2b8615e..91210d2ea5 100644
--- a/modules/snan-tests
+++ b/modules/snan-tests
@@ -3,12 +3,14 @@ tests/test-snan-1.c
 tests/test-snan-2.sh
 tests/test-snan-2.c
 tests/macros.h
+m4/math_h.m4
 
 Depends-on:
 fpe-tracking
 fpe-trapping
 
 configure.ac:
+AC_REQUIRE([gl_LONG_DOUBLE_VS_DOUBLE])
 
 Makefile.am:
 TESTS += test-snan-1 test-snan-2.sh
diff --git a/tests/test-nan-1.c b/tests/test-nan-1.c
index 9bce3225c1..4ebe013e3c 100644
--- a/tests/test-nan-1.c
+++ b/tests/test-nan-1.c
@@ -21,11 +21,27 @@
 /* Specification.  */
 #include "nan.h"
 
+#include <stdio.h>
+
 #if HAVE_FE_INVALID
 
-# include <fenv.h>
+# if defined __GLIBC__ && defined __arm__ && defined __SOFTFP__
+
+/* The arm software floating-point emulation (used e.g. on armv5) does not set
+   the floating-point exception bits.  */
+
+int
+main ()
+{
+  fputs ("Skipping test: software floating-point emulation\n", stderr);
+  return 77;
+}
+
+# else
+
+#  include <fenv.h>
 
-# include "macros.h"
+#  include "macros.h"
 
 float volatile resultf;
 double volatile resultd;
@@ -64,14 +80,14 @@ main ()
   return 0;
 }
 
+# endif
+
 #else
 
 /* No <fenv.h> available.
    We could use the various alternative approaches from
    libgfortran/config/fpu-*.h, but that's not worth it.  */
 
-#include <stdio.h>
-
 int
 main ()
 {
diff --git a/tests/test-snan-1.c b/tests/test-snan-1.c
index de87372f17..fc8fc99e31 100644
--- a/tests/test-snan-1.c
+++ b/tests/test-snan-1.c
@@ -21,11 +21,27 @@
 /* Specification.  */
 #include "snan.h"
 
+#include <stdio.h>
+
 #if HAVE_FE_INVALID
 
-# include <fenv.h>
+# if defined __GLIBC__ && defined __arm__ && defined __SOFTFP__
+
+/* The arm software floating-point emulation (used e.g. on armv5) does not set
+   the floating-point exception bits.  */
+
+int
+main ()
+{
+  fputs ("Skipping test: software floating-point emulation\n", stderr);
+  return 77;
+}
+
+# else
+
+#  include <fenv.h>
 
-# include "macros.h"
+#  include "macros.h"
 
 float volatile resultf;
 double volatile resultd;
@@ -45,33 +61,60 @@ main ()
   long double volatile nanl = SNaNl ();
 
   /* Check that the values are really signalling.  */
+  /* These tests do not work on 32-bit x86 processors, although the
+     "Intel 64 and IA-32 Architectures Software Developer's Manual" and
+     "IA-32 Intel Architecture Software Developer's Manual", each in
+     sections
+       4.8.3.4 NaNs
+       4.8.3.5 Operating on SNaNs and QNaNs
+       4.8.3.6 Using SNaNs and QNaNs in Applications
+     claim that it should work.  */
+  #if !(defined __i386 || defined _M_IX86)
+  /* This test does not work on AIX 7.1 with the xlc compiler, even with
+     the compiler options -qfloat=fenv -qfloat=nans -qfloat=spnans.  */
+  #if !(defined _AIX && defined __xlC__)
   {
     feclearexcept (FE_INVALID);
     resultf = nanf + 42.0f;
     ASSERT (fetestexcept (FE_INVALID));
   }
+  #endif
   {
     feclearexcept (FE_INVALID);
     resultd = nand + 42.0;
     ASSERT (fetestexcept (FE_INVALID));
   }
+  #endif
+  /* This test does not work on eglibc 2.13/mips64
+     (bug in libc function __addtf3).
+     This test does not work on FreeBSD/arm64
+     (bug in libc function __addtf3).
+     This test does not work on FreeBSD/sparc64 and NetBSD/sparc64
+     (bug in libc function _Qp_add).
+     This test does not work on MSVC/i386, because of the general IA-32
+     problem (see above) and 'long double' == 'double'.  */
+  #if !((((__GLIBC__ == 2 && __GLIBC_MINOR__ < 19 && defined __mips64) \
+          || ((defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__) && (defined __aarch64__ || defined __sparc__))) \
+         && !HAVE_SAME_LONG_DOUBLE_AS_DOUBLE) \
+        || ((defined __i386 || defined _M_IX86) && HAVE_SAME_LONG_DOUBLE_AS_DOUBLE))
   {
     feclearexcept (FE_INVALID);
     resultl = nanl + 42.0L;
     ASSERT (fetestexcept (FE_INVALID));
   }
+  #endif
 
   return 0;
 }
 
+# endif
+
 #else
 
 /* No <fenv.h> available.
    We could use the various alternative approaches from
    libgfortran/config/fpu-*.h, but that's not worth it.  */
 
-#include <stdio.h>
-
 int
 main ()
 {
diff --git a/tests/test-snan-2.c b/tests/test-snan-2.c
index e1ee7c8928..42927d6077 100644
--- a/tests/test-snan-2.c
+++ b/tests/test-snan-2.c
@@ -65,14 +65,74 @@ main (int argc, char *argv[])
     switch (argv[1][0])
       {
       case 'f':
+        /* This test does not work on 32-bit x86 processors, although the
+           "Intel 64 and IA-32 Architectures Software Developer's Manual" and
+           "IA-32 Intel Architecture Software Developer's Manual", each in
+           sections
+             4.8.3.4 NaNs
+             4.8.3.5 Operating on SNaNs and QNaNs
+             4.8.3.6 Using SNaNs and QNaNs in Applications
+           claim that it should work.  */
+        #if !(defined __i386 || defined _M_IX86)
+        /* This test does not work on AIX 7.1 with the xlc compiler, even with
+           the compiler options -qfloat=fenv -qfloat=nans -qfloat=spnans.  */
+        #if !(defined _AIX && defined __xlC__)
         resultf = nanf + 42.0f;
+        #else
+        fputs ("Skipping test: known failure on this platform with this compiler\n", stderr);
+        return 77;
+        #endif
+        #else
+        fputs ("Skipping test: known failure on this platform\n", stderr);
+        return 77;
+        #endif
         break;
+
       case 'd':
+        /* This test does not work on 32-bit x86 processors, although the
+           "Intel 64 and IA-32 Architectures Software Developer's Manual" and
+           "IA-32 Intel Architecture Software Developer's Manual", each in
+           sections
+             4.8.3.4 NaNs
+             4.8.3.5 Operating on SNaNs and QNaNs
+             4.8.3.6 Using SNaNs and QNaNs in Applications
+           claim that it should work.  */
+        #if !(defined __i386 || defined _M_IX86)
         resultd = nand + 42.0;
+        #else
+        fputs ("Skipping test: known failure on this platform\n", stderr);
+        return 77;
+        #endif
         break;
+
       case 'l':
+        /* This test does not work on Linux/alpha with glibc 2.7.  But it
+           works with glibc 2.36.  Cause unknown.
+           This test does not work on Linux/loongarch64 with glibc 2.37.
+           Cause unknown.
+           This test does not work on eglibc 2.13/mips64
+           (bug in libc function __addtf3).
+           This test does not work on FreeBSD/arm64
+           (bug in libc function __addtf3).
+           This test does not work on FreeBSD/sparc64 and NetBSD/sparc64
+           (bug in libc function _Qp_add).
+           This test does not work on Cygwin 2.9.0/i386. Cause unknown.
+           This test does not work on MSVC/i386, because of the general IA-32
+           problem (see above) and 'long double' == 'double'.  */
+        #if !((__GLIBC__ == 2 && __GLIBC_MINOR__ < 36 && defined __alpha__) \
+              || (__GLIBC__ >= 2 && defined __loongarch__) \
+              || (((__GLIBC__ == 2 && __GLIBC_MINOR__ < 19 && defined __mips64) \
+                   || ((defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__) && (defined __aarch64__ || defined __sparc__))) \
+                  && !HAVE_SAME_LONG_DOUBLE_AS_DOUBLE) \
+              || (defined __CYGWIN__ && defined __i386) \
+              || ((defined __i386 || defined _M_IX86) && HAVE_SAME_LONG_DOUBLE_AS_DOUBLE))
         resultl = nanl + 42.0L;
+        #else
+        fputs ("Skipping test: known failure on this platform\n", stderr);
+        return 77;
+        #endif
         break;
+
       default:
         break;
       }
-- 
2.34.1

Reply via email to