]> Savannah Git Hosting - gnulib.git/commitdiff
fenv-exceptions-tracking-c99: Add tests.
authorBruno Haible <bruno@clisp.org>
Sat, 28 Oct 2023 16:39:32 +0000 (18:39 +0200)
committerBruno Haible <bruno@clisp.org>
Sat, 28 Oct 2023 16:41:06 +0000 (18:41 +0200)
* tests/test-fenv-except-tracking-1.c: New file.
* tests/test-fenv-except-tracking-2.sh: New file.
* tests/test-fenv-except-tracking-2.c: New file.
* tests/test-fenv-except-tracking-3.sh: New file.
* tests/test-fenv-except-tracking-3.c: New file.
* modules/fenv-exceptions-tracking-c99-tests: New file.

ChangeLog
modules/fenv-exceptions-tracking-c99-tests [new file with mode: 0644]
tests/test-fenv-except-tracking-1.c [new file with mode: 0644]
tests/test-fenv-except-tracking-2.c [new file with mode: 0644]
tests/test-fenv-except-tracking-2.sh [new file with mode: 0755]
tests/test-fenv-except-tracking-3.c [new file with mode: 0644]
tests/test-fenv-except-tracking-3.sh [new file with mode: 0755]

index b1c73c0f9800b47c2f99b51e9264a50d513bbaa1..3af21529c2ff4147e1312281a91bc8a7c8b254af 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,13 @@
 2023-10-28  Bruno Haible  <bruno@clisp.org>
 
+       fenv-exceptions-tracking-c99: Add tests.
+       * tests/test-fenv-except-tracking-1.c: New file.
+       * tests/test-fenv-except-tracking-2.sh: New file.
+       * tests/test-fenv-except-tracking-2.c: New file.
+       * tests/test-fenv-except-tracking-3.sh: New file.
+       * tests/test-fenv-except-tracking-3.c: New file.
+       * modules/fenv-exceptions-tracking-c99-tests: New file.
+
        fenv-exceptions-tracking-c99: New module.
        * lib/fenv.in.h (feclearexcept, feraiseexcept, fetestexcept): New
        declarations.
diff --git a/modules/fenv-exceptions-tracking-c99-tests b/modules/fenv-exceptions-tracking-c99-tests
new file mode 100644 (file)
index 0000000..8c1dde1
--- /dev/null
@@ -0,0 +1,27 @@
+Files:
+tests/test-fenv-except-tracking-1.c
+tests/test-fenv-except-tracking-2.sh
+tests/test-fenv-except-tracking-2.c
+tests/test-fenv-except-tracking-3.sh
+tests/test-fenv-except-tracking-3.c
+tests/macros.h
+m4/musl.m4
+
+Depends-on:
+fpe-trapping
+
+configure.ac:
+gl_MUSL_LIBC
+
+Makefile.am:
+TESTS += \
+  test-fenv-except-tracking-1 \
+  test-fenv-except-tracking-2.sh \
+  test-fenv-except-tracking-3.sh
+check_PROGRAMS += \
+  test-fenv-except-tracking-1 \
+  test-fenv-except-tracking-2 \
+  test-fenv-except-tracking-3
+test_fenv_except_tracking_1_LDADD = $(LDADD) @FENV_EXCEPTIONS_TRACKING_LIBM@
+test_fenv_except_tracking_2_LDADD = $(LDADD) @FENV_EXCEPTIONS_TRACKING_LIBM@ @FPE_TRAPPING_LIBM@
+test_fenv_except_tracking_3_LDADD = $(LDADD) @FENV_EXCEPTIONS_TRACKING_LIBM@ @FPE_TRAPPING_LIBM@
diff --git a/tests/test-fenv-except-tracking-1.c b/tests/test-fenv-except-tracking-1.c
new file mode 100644 (file)
index 0000000..b7f871e
--- /dev/null
@@ -0,0 +1,250 @@
+/* Test of tracking of 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/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2023.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include <fenv.h>
+
+#include "macros.h"
+
+static volatile double a, b, c;
+
+int
+main ()
+{
+  /* Test setting all exception flags.  */
+  if (feraiseexcept (FE_ALL_EXCEPT) != 0)
+    {
+      fputs ("Skipping test: floating-point exceptions are not supported on this machine.\n", stderr);
+      return 77;
+    }
+  ASSERT (/* with the libc's feraiseexcept(): */
+          fetestexcept (FE_ALL_EXCEPT) == FE_ALL_EXCEPT
+          || /* with gnulib's feraiseexcept(): */
+             fetestexcept (FE_ALL_EXCEPT)
+             == (FE_DIVBYZERO | FE_INEXACT | FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW));
+  ASSERT (fetestexcept (FE_INVALID) == FE_INVALID);
+  ASSERT (fetestexcept (FE_DIVBYZERO) == FE_DIVBYZERO);
+  ASSERT (fetestexcept (FE_OVERFLOW) == FE_OVERFLOW);
+  ASSERT (fetestexcept (FE_UNDERFLOW) == FE_UNDERFLOW);
+  ASSERT (fetestexcept (FE_INEXACT) == FE_INEXACT);
+
+  /* Test clearing all exception flags.  */
+  ASSERT (feclearexcept (FE_ALL_EXCEPT) == 0);
+  ASSERT (fetestexcept (FE_ALL_EXCEPT) == 0);
+  ASSERT (fetestexcept (FE_INVALID) == 0);
+  ASSERT (fetestexcept (FE_DIVBYZERO) == 0);
+  ASSERT (fetestexcept (FE_OVERFLOW) == 0);
+  ASSERT (fetestexcept (FE_UNDERFLOW) == 0);
+  ASSERT (fetestexcept (FE_INEXACT) == 0);
+
+
+  /* Test setting just one exception flag: FE_INVALID.  */
+  ASSERT (feclearexcept (FE_ALL_EXCEPT) == 0);
+  ASSERT (feraiseexcept (FE_INVALID) == 0);
+  ASSERT (fetestexcept (FE_ALL_EXCEPT) == FE_INVALID);
+  ASSERT (fetestexcept (FE_INVALID) == FE_INVALID);
+  ASSERT (fetestexcept (FE_DIVBYZERO) == 0);
+  ASSERT (fetestexcept (FE_OVERFLOW) == 0);
+  ASSERT (fetestexcept (FE_UNDERFLOW) == 0);
+  ASSERT (fetestexcept (FE_INEXACT) == 0);
+
+  /* Test setting just one exception flag: FE_DIVBYZERO.  */
+  ASSERT (feclearexcept (FE_ALL_EXCEPT) == 0);
+  ASSERT (feraiseexcept (FE_DIVBYZERO) == 0);
+  ASSERT (fetestexcept (FE_ALL_EXCEPT) == FE_DIVBYZERO);
+  ASSERT (fetestexcept (FE_INVALID) == 0);
+  ASSERT (fetestexcept (FE_DIVBYZERO) == FE_DIVBYZERO);
+  ASSERT (fetestexcept (FE_OVERFLOW) == 0);
+  ASSERT (fetestexcept (FE_UNDERFLOW) == 0);
+  ASSERT (fetestexcept (FE_INEXACT) == 0);
+
+  /* Test setting just one exception flag: FE_OVERFLOW.
+     On many architectures, this has the side-effect of also setting FE_INEXACT:
+     arm64, arm, alpha, hppa, ia64, loongarch64, s390, sh, sparc.  */
+  ASSERT (feclearexcept (FE_ALL_EXCEPT) == 0);
+  ASSERT (feraiseexcept (FE_OVERFLOW) == 0);
+  ASSERT ((fetestexcept (FE_ALL_EXCEPT) & ~FE_INEXACT) == FE_OVERFLOW);
+  ASSERT (fetestexcept (FE_INVALID) == 0);
+  ASSERT (fetestexcept (FE_DIVBYZERO) == 0);
+  ASSERT (fetestexcept (FE_OVERFLOW) == FE_OVERFLOW);
+  ASSERT (fetestexcept (FE_UNDERFLOW) == 0);
+
+  /* Test setting just one exception flag: FE_UNDERFLOW.
+     On many architectures, this has the side-effect of also setting FE_INEXACT:
+     arm64, arm, alpha, hppa, ia64, loongarch64, s390, sh, sparc.  */
+  ASSERT (feclearexcept (FE_ALL_EXCEPT) == 0);
+  ASSERT (feraiseexcept (FE_UNDERFLOW) == 0);
+  ASSERT ((fetestexcept (FE_ALL_EXCEPT) & ~FE_INEXACT) == FE_UNDERFLOW);
+  ASSERT (fetestexcept (FE_INVALID) == 0);
+  ASSERT (fetestexcept (FE_DIVBYZERO) == 0);
+  ASSERT (fetestexcept (FE_OVERFLOW) == 0);
+  ASSERT (fetestexcept (FE_UNDERFLOW) == FE_UNDERFLOW);
+
+  /* Test setting just one exception flag: FE_INEXACT.  */
+  ASSERT (feclearexcept (FE_ALL_EXCEPT) == 0);
+  ASSERT (feraiseexcept (FE_INEXACT) == 0);
+  ASSERT (fetestexcept (FE_ALL_EXCEPT) == FE_INEXACT);
+  ASSERT (fetestexcept (FE_INVALID) == 0);
+  ASSERT (fetestexcept (FE_DIVBYZERO) == 0);
+  ASSERT (fetestexcept (FE_OVERFLOW) == 0);
+  ASSERT (fetestexcept (FE_UNDERFLOW) == 0);
+  ASSERT (fetestexcept (FE_INEXACT) == FE_INEXACT);
+
+
+  /* Test clearing just one exception flag: FE_INVALID.  */
+  ASSERT (feraiseexcept (FE_ALL_EXCEPT) == 0);
+  ASSERT (feclearexcept (FE_INVALID) == 0);
+  ASSERT (/* with the libc's feraiseexcept(): */
+          fetestexcept (FE_ALL_EXCEPT) == (FE_ALL_EXCEPT & ~FE_INVALID)
+          || /* with gnulib's feraiseexcept(): */
+             fetestexcept (FE_ALL_EXCEPT)
+             == (FE_DIVBYZERO | FE_INEXACT | FE_OVERFLOW | FE_UNDERFLOW));
+  ASSERT (fetestexcept (FE_INVALID) == 0);
+  ASSERT (fetestexcept (FE_DIVBYZERO) == FE_DIVBYZERO);
+  ASSERT (fetestexcept (FE_OVERFLOW) == FE_OVERFLOW);
+  ASSERT (fetestexcept (FE_UNDERFLOW) == FE_UNDERFLOW);
+  ASSERT (fetestexcept (FE_INEXACT) == FE_INEXACT);
+
+  /* Test clearing just one exception flag: FE_DIVBYZERO.  */
+  ASSERT (feraiseexcept (FE_ALL_EXCEPT) == 0);
+  ASSERT (feclearexcept (FE_DIVBYZERO) == 0);
+  ASSERT (/* with the libc's feraiseexcept(): */
+          fetestexcept (FE_ALL_EXCEPT) == (FE_ALL_EXCEPT & ~FE_DIVBYZERO)
+          || /* with gnulib's feraiseexcept(): */
+             fetestexcept (FE_ALL_EXCEPT)
+             == (FE_INEXACT | FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW));
+  ASSERT (fetestexcept (FE_INVALID) == FE_INVALID);
+  ASSERT (fetestexcept (FE_DIVBYZERO) == 0);
+  ASSERT (fetestexcept (FE_OVERFLOW) == FE_OVERFLOW);
+  ASSERT (fetestexcept (FE_UNDERFLOW) == FE_UNDERFLOW);
+  ASSERT (fetestexcept (FE_INEXACT) == FE_INEXACT);
+
+  /* Test clearing just one exception flag: FE_OVERFLOW.  */
+  ASSERT (feraiseexcept (FE_ALL_EXCEPT) == 0);
+  ASSERT (feclearexcept (FE_OVERFLOW) == 0);
+  ASSERT (/* with the libc's feraiseexcept(): */
+          fetestexcept (FE_ALL_EXCEPT) == (FE_ALL_EXCEPT & ~FE_OVERFLOW)
+          || /* with gnulib's feraiseexcept(): */
+             fetestexcept (FE_ALL_EXCEPT)
+             == (FE_DIVBYZERO | FE_INEXACT | FE_INVALID | FE_UNDERFLOW));
+  ASSERT (fetestexcept (FE_INVALID) == FE_INVALID);
+  ASSERT (fetestexcept (FE_DIVBYZERO) == FE_DIVBYZERO);
+  ASSERT (fetestexcept (FE_OVERFLOW) == 0);
+  ASSERT (fetestexcept (FE_UNDERFLOW) == FE_UNDERFLOW);
+  ASSERT (fetestexcept (FE_INEXACT) == FE_INEXACT);
+
+  /* Test clearing just one exception flag: FE_UNDERFLOW.  */
+  ASSERT (feraiseexcept (FE_ALL_EXCEPT) == 0);
+  ASSERT (feclearexcept (FE_UNDERFLOW) == 0);
+  ASSERT (/* with the libc's feraiseexcept(): */
+          fetestexcept (FE_ALL_EXCEPT) == (FE_ALL_EXCEPT & ~FE_UNDERFLOW)
+          || /* with gnulib's feraiseexcept(): */
+             fetestexcept (FE_ALL_EXCEPT)
+             == (FE_DIVBYZERO | FE_INEXACT | FE_INVALID | FE_OVERFLOW));
+  ASSERT (fetestexcept (FE_INVALID) == FE_INVALID);
+  ASSERT (fetestexcept (FE_DIVBYZERO) == FE_DIVBYZERO);
+  ASSERT (fetestexcept (FE_OVERFLOW) == FE_OVERFLOW);
+  ASSERT (fetestexcept (FE_UNDERFLOW) == 0);
+  ASSERT (fetestexcept (FE_INEXACT) == FE_INEXACT);
+
+  /* Test clearing just one exception flag: FE_INEXACT.  */
+  ASSERT (feraiseexcept (FE_ALL_EXCEPT) == 0);
+  ASSERT (feclearexcept (FE_INEXACT) == 0);
+  ASSERT (/* with the libc's feraiseexcept(): */
+          fetestexcept (FE_ALL_EXCEPT) == (FE_ALL_EXCEPT & ~FE_INEXACT)
+          || /* with gnulib's feraiseexcept(): */
+             fetestexcept (FE_ALL_EXCEPT)
+             == (FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW));
+  ASSERT (fetestexcept (FE_INVALID) == FE_INVALID);
+  ASSERT (fetestexcept (FE_DIVBYZERO) == FE_DIVBYZERO);
+  ASSERT (fetestexcept (FE_OVERFLOW) == FE_OVERFLOW);
+  ASSERT (fetestexcept (FE_UNDERFLOW) == FE_UNDERFLOW);
+  ASSERT (fetestexcept (FE_INEXACT) == 0);
+
+
+#if !(defined __arm__ && defined __SOFTFP__)
+  /* Test the effects of an operation that produces FE_INVALID.  */
+  ASSERT (feclearexcept (FE_ALL_EXCEPT) == 0);
+  a = 0; b = 0; c = a / b;
+  ASSERT (fetestexcept (FE_ALL_EXCEPT) == FE_INVALID);
+  ASSERT (fetestexcept (FE_INVALID) == FE_INVALID);
+  ASSERT (fetestexcept (FE_DIVBYZERO) == 0);
+  ASSERT (fetestexcept (FE_OVERFLOW) == 0);
+  ASSERT (fetestexcept (FE_UNDERFLOW) == 0);
+  ASSERT (fetestexcept (FE_INEXACT) == 0);
+
+  /* Test the effects of an operation that produces FE_DIVBYZERO.  */
+  ASSERT (feclearexcept (FE_ALL_EXCEPT) == 0);
+  a = 3; b = 0; c = a / b;
+  ASSERT (fetestexcept (FE_ALL_EXCEPT) == FE_DIVBYZERO);
+  ASSERT (fetestexcept (FE_INVALID) == 0);
+  ASSERT (fetestexcept (FE_DIVBYZERO) == FE_DIVBYZERO);
+  ASSERT (fetestexcept (FE_OVERFLOW) == 0);
+  ASSERT (fetestexcept (FE_UNDERFLOW) == 0);
+  ASSERT (fetestexcept (FE_INEXACT) == 0);
+
+  /* Test the effects of an operation that produces FE_OVERFLOW.  */
+  ASSERT (feclearexcept (FE_ALL_EXCEPT) == 0);
+  a = 1; for (int i = 0; i < 800; i++) a = 2.0 * a;
+  b = 1; for (int i = 0; i < 700; i++) b = 2.0 * b;
+  c = a * b;
+  {
+    int exc = fetestexcept (FE_ALL_EXCEPT);
+    ASSERT ((FE_OVERFLOW & !exc) == 0);
+    ASSERT ((exc & ~(FE_OVERFLOW | FE_INEXACT)) == 0);
+  }
+  ASSERT (fetestexcept (FE_INVALID) == 0);
+  ASSERT (fetestexcept (FE_DIVBYZERO) == 0);
+  ASSERT (fetestexcept (FE_OVERFLOW) == FE_OVERFLOW);
+  ASSERT (fetestexcept (FE_UNDERFLOW) == 0);
+
+  /* Test the effects of an operation that produces FE_UNDERFLOW.  */
+  ASSERT (feclearexcept (FE_ALL_EXCEPT) == 0);
+  a = 1; for (int i = 0; i < 800; i++) a = 0.5 * a;
+  b = 1; for (int i = 0; i < 700; i++) b = 0.5 * b;
+  c = a * b;
+  {
+    int exc = fetestexcept (FE_ALL_EXCEPT);
+    ASSERT ((FE_UNDERFLOW & !exc) == 0);
+    ASSERT ((exc & ~(FE_UNDERFLOW | FE_INEXACT)) == 0);
+  }
+  ASSERT (fetestexcept (FE_INVALID) == 0);
+  ASSERT (fetestexcept (FE_DIVBYZERO) == 0);
+  ASSERT (fetestexcept (FE_OVERFLOW) == 0);
+  ASSERT (fetestexcept (FE_UNDERFLOW) == FE_UNDERFLOW);
+
+/* On alpha, this test works only when compiled with the GCC option
+   '-mieee-with-inexact'.  */
+# if !defined __alpha
+  /* Test the effects of an operation that produces FE_INEXACT.  */
+  ASSERT (feclearexcept (FE_ALL_EXCEPT) == 0);
+  a = 1; b = 3; c = a / b;
+  ASSERT (fetestexcept (FE_ALL_EXCEPT) == FE_INEXACT);
+  ASSERT (fetestexcept (FE_INVALID) == 0);
+  ASSERT (fetestexcept (FE_DIVBYZERO) == 0);
+  ASSERT (fetestexcept (FE_OVERFLOW) == 0);
+  ASSERT (fetestexcept (FE_UNDERFLOW) == 0);
+  ASSERT (fetestexcept (FE_INEXACT) == FE_INEXACT);
+# endif
+#endif
+
+
+  return 0;
+}
diff --git a/tests/test-fenv-except-tracking-2.c b/tests/test-fenv-except-tracking-2.c
new file mode 100644 (file)
index 0000000..e6f9bf1
--- /dev/null
@@ -0,0 +1,101 @@
+/* Test of tracking of 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/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2023.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include <fenv.h>
+
+#include <stdio.h>
+
+#include "fpe-trapping.h"
+#include "macros.h"
+
+#if HAVE_FPE_TRAPPING
+
+/* Check that triggering a floating-point operation can trigger a trap.  */
+
+int
+main (int argc, char *argv[])
+{
+  /* 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':
+        {
+          volatile float a, b;
+          _GL_UNUSED volatile float c;
+          a = 0; b = 0; c = a / b;
+        }
+        break;
+
+      case 'd':
+        {
+          volatile double a, b;
+          _GL_UNUSED volatile double c;
+          a = 0; b = 0; c = a / b;
+        }
+        break;
+
+      case 'l':
+        /* This test does 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))
+        {
+          volatile long double a, b;
+          _GL_UNUSED volatile long double c;
+          a = 0; b = 0; c = a / b;
+        }
+        #else
+        fputs ("Skipping test: known failure on this platform\n", stderr);
+        return 77;
+        #endif
+        break;
+
+      default:
+        break;
+      }
+
+  return 0;
+}
+
+#else
+
+int
+main ()
+{
+  fputs ("Skipping test: feenableexcept or fpsetmask or fp_enable not available\n", stderr);
+  return 77;
+}
+
+#endif
diff --git a/tests/test-fenv-except-tracking-2.sh b/tests/test-fenv-except-tracking-2.sh
new file mode 100755 (executable)
index 0000000..deb3ed6
--- /dev/null
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+# Test that a floating-point operation can trigger a trap.
+
+final_rc=0
+
+for arg in f d l; do
+  ${CHECKER} ./test-fenv-except-tracking-2${EXEEXT} $arg
+  rc=$?
+  if test $rc = 77; then
+    final_rc=77
+  else
+    if test $rc = 0; then
+      echo "Failed: ./test-fenv-except-tracking-2 $arg" 1>&2
+      exit 1
+    fi
+  fi
+done
+
+exit $final_rc
diff --git a/tests/test-fenv-except-tracking-3.c b/tests/test-fenv-except-tracking-3.c
new file mode 100644 (file)
index 0000000..a7ef77a
--- /dev/null
@@ -0,0 +1,64 @@
+/* Test of tracking of 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/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2023.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include <fenv.h>
+
+#include <stdio.h>
+
+#include "fpe-trapping.h"
+#include "macros.h"
+
+/* musl libc does not support floating-point exception trapping, even where
+   the hardware supports it.  See
+   <https://wiki.musl-libc.org/functional-differences-from-glibc.html>  */
+#if HAVE_FPE_TRAPPING && (!MUSL_LIBC || GNULIB_FEENABLEEXCEPT)
+
+/* Check that feraiseexcept() can trigger a trap.  */
+
+int
+main ()
+{
+  /* 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;
+    }
+
+  feraiseexcept (FE_INVALID);
+
+  return 0;
+}
+
+#else
+
+int
+main ()
+{
+  fputs ("Skipping test: feenableexcept or fpsetmask or fp_enable not available\n", stderr);
+  return 77;
+}
+
+#endif
diff --git a/tests/test-fenv-except-tracking-3.sh b/tests/test-fenv-except-tracking-3.sh
new file mode 100755 (executable)
index 0000000..f44e43a
--- /dev/null
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+# Test that feraiseexcept() can trigger a trap.
+
+final_rc=0
+
+${CHECKER} ./test-fenv-except-tracking-3${EXEEXT}
+rc=$?
+if test $rc = 77; then
+  final_rc=77
+else
+  if test $rc = 0; then
+    echo "Failed: ./test-fenv-except-tracking-3" 1>&2
+    exit 1
+  fi
+fi
+
+exit $final_rc