When writing the lib/fpe-trapping.h file (needed to verify the quiet vs.
signalling NaNs, which in turn was needed for testing the totalorder*
functions), I was disappointed by the complexity of this file. Ideally
it would contain just two statements:
  feclearexcept (FE_INVALID);
  feenableexcept (FE_INVALID);
but then various portability problems were encountered.

Following the Gnulib philosophy, these portability fixes should be
hidden behind a GNU-like <fenv.h>.

Here comes the module for the <fenv.h> header file. More modules to
come in the next days.

There is special code for each CPU, because many CPUs have their
own quirks:
  - x86_64 has two floating point units (387 unit and SSE unit).
    Recall the 387 unit's "we compute everything with 80-bits
    'long double's, whether you like it or not" philosophy, that
    has killed the reproducibility of numerical programs' results
    for decades.
  - i386 has one or two floating point units (387 always, SSE unit often).
  - aarch64 may or may not support trapping on floating-point
    exceptions.
  - arm likewise. Also armv5 has only software float routines, with
    no support for floating-point exceptions and only one rounding
    direction.
  - riscv does not support trapping on floating-point exceptions at all.
  - alpha does not support "inexact" floating-point exceptions well.
    It also requires system calls to modify the floating-point
    control word.
  - m68k has 3 bits for "Invalid operation", not just one. Also it
    has 'double's that deviate from IEEE 754.
  - powerpc has its control word stored in a floating-point register,
    not in an integer-like register.
  - hppa likewise.
  - sh4 has only two rounding directions, not four.
  - ia64, like x86_64 and i386, has bits that are set when traps
    for a certain floating-point exception are *disabled* (the
    opposite of all other CPUs).
Only mips, s390, sparc, loongarch are somewhat sane.

Bruno


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

        fenv: Add tests.
        * tests/test-fenv.c: New file.
        * modules/fenv-tests: New file.

        fenv: New module.
        * lib/fenv.in.h: New file, based on glibc.
        * m4/fenv_h.m4: New file.
        * modules/fenv: New file.
        * doc/posix-headers/fenv.texi: Mention the new module.

From b536e9d30d31345f197ccf219378ed3142fc6beb Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Fri, 27 Oct 2023 03:43:15 +0200
Subject: [PATCH 1/2] fenv: New module.

* lib/fenv.in.h: New file, based on glibc.
* m4/fenv_h.m4: New file.
* modules/fenv: New file.
* doc/posix-headers/fenv.texi: Mention the new module.
---
 ChangeLog                   |   8 +
 doc/posix-headers/fenv.texi |   8 +-
 lib/fenv.in.h               | 489 ++++++++++++++++++++++++++++++++++++
 m4/fenv_h.m4                |  98 ++++++++
 modules/fenv                |  87 +++++++
 5 files changed, 686 insertions(+), 4 deletions(-)
 create mode 100644 lib/fenv.in.h
 create mode 100644 m4/fenv_h.m4
 create mode 100644 modules/fenv

diff --git a/ChangeLog b/ChangeLog
index fed7803763..b89af7a969 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2023-10-26  Bruno Haible  <br...@clisp.org>
+
+	fenv: New module.
+	* lib/fenv.in.h: New file, based on glibc.
+	* m4/fenv_h.m4: New file.
+	* modules/fenv: New file.
+	* doc/posix-headers/fenv.texi: Mention the new module.
+
 2023-10-25  Paul Eggert  <egg...@cs.ucla.edu>
 
 	base32: new function isubase32; also, tune.
diff --git a/doc/posix-headers/fenv.texi b/doc/posix-headers/fenv.texi
index 2e7d10d29e..36c636d7db 100644
--- a/doc/posix-headers/fenv.texi
+++ b/doc/posix-headers/fenv.texi
@@ -3,15 +3,15 @@
 
 POSIX specification:@* @url{https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/fenv.h.html}
 
-Gnulib module: ---
+Gnulib module: fenv
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This header file is missing on some platforms:
+FreeBSD 5.2.1, NetBSD 5.0, OpenBSD 3.8, AIX 5.1, IRIX 6.5, Cygwin 1.7.7, MSVC 9.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
-@item
-This header file is missing on some platforms:
-FreeBSD 5.2.1, NetBSD 5.0, OpenBSD 3.8, AIX 5.1, IRIX 6.5, Cygwin 1.7.7, MSVC 9.
 @end itemize
diff --git a/lib/fenv.in.h b/lib/fenv.in.h
new file mode 100644
index 0000000000..c52d0e0ccb
--- /dev/null
+++ b/lib/fenv.in.h
@@ -0,0 +1,489 @@
+/* A GNU-like <fenv.h>.
+
+   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>/bits/fenv.h.  */
+
+#if __GNUC__ >= 3
+@PRAGMA_SYSTEM_HEADER@
+#endif
+@PRAGMA_COLUMNS@
+
+#ifndef _@GUARD_PREFIX@_FENV_H
+
+/* The include_next requires a split double-inclusion guard.  */
+#if @HAVE_FENV_H@
+# @INCLUDE_NEXT@ @NEXT_FENV_H@
+#endif
+
+#ifndef _@GUARD_PREFIX@_FENV_H
+#define _@GUARD_PREFIX@_FENV_H
+
+/* This file uses GNULIB_POSIXCHECK.  */
+#if !_GL_CONFIG_H_INCLUDED
+ #error "Please include config.h first."
+#endif
+
+/* The definition of _GL_WARN_ON_USE is copied here.  */
+
+
+/* We cannot implement the #pragmas here; this needs to be done in the
+   compiler.  */
+
+
+/* ISO C 99 § 7.6.4 Environment
+   ISO C 23 § 7.6.6 Environment  */
+
+#if !@HAVE_FENV_H@
+
+# if (defined __x86_64__ || defined _M_X64) || (defined __i386 || defined _M_IX86)
+
+typedef struct
+  {
+    /* 7 32-bit words, see <https://stackoverflow.com/questions/10337750/>  */
+    unsigned short __control_word;         /* fctrl register */
+    unsigned short __reserved1;
+    unsigned short __status_word;          /* fstat register */
+    unsigned short __reserved2;
+    unsigned short __tags;
+    unsigned short __reserved3;
+    unsigned int __instruction_pointer;    /* eip register */
+    unsigned short __instruction_selector; /* cs register */
+    unsigned int __opcode : 11;
+    unsigned int __reserved4 : 5;
+    unsigned int __data_pointer;
+    unsigned short __data_selector;
+    unsigned short __reserved5;
+#  ifdef __x86_64__
+    unsigned int __mxcsr;                  /* mxcsr register */
+#  endif
+  }
+fenv_t;
+
+# elif defined __aarch64__ /* arm64 */
+
+typedef struct
+  {
+    unsigned int __fpcr; /* fpcr register */
+    unsigned int __fpsr; /* fpsr register */
+  }
+fenv_t;
+
+# elif defined __arm__
+
+typedef struct
+  {
+    unsigned int __cw; /* fpscr register */
+  }
+fenv_t;
+
+# elif defined __alpha
+
+typedef unsigned long fenv_t;
+
+# elif defined __hppa
+
+typedef struct
+{
+  unsigned int __status_word;  /* floating point status register */
+  unsigned int __exception[7]; /* floating point exception registers */
+} fenv_t;
+
+# elif defined __ia64__
+
+typedef unsigned long fenv_t; /* fpsr = floating point status register */
+
+# elif defined __m68k__
+
+typedef struct
+  {
+    unsigned int __control_register;    /* fpcr = floating point control register */
+    unsigned int __status_register;     /* fpsr = floating point status register */
+    unsigned int __instruction_address; /* floating point instruction register */
+  }
+fenv_t;
+
+# elif defined __mips__ || defined __loongarch__
+
+typedef struct
+  {
+    unsigned int __fp_control_register; /* floating point control and status register */
+  }
+fenv_t;
+
+# elif defined __powerpc__
+
+typedef double /* yuck! */ fenv_t; /* fpscr register */
+
+# elif defined __riscv
+
+typedef unsigned int fenv_t; /* fcsr register */
+
+# elif defined __s390__ || defined __s390x__
+
+typedef struct
+  {
+    unsigned int __fpc; /* fpc = floating point control register */
+  }
+fenv_t;
+
+# elif defined __sh__
+
+typedef struct
+  {
+    unsigned int __fpscr; /* fpscr register */
+  }
+fenv_t;
+
+# elif defined __sparc
+
+typedef unsigned long fenv_t; /* fsr = floating point state register */
+
+#else
+
+/* A dummy fallback.  */
+
+typedef unsigned long fenv_t;
+
+# endif
+
+# define FE_DFL_ENV ((const fenv_t *) (-1))
+
+#endif
+
+
+/* ISO C 99 § 7.6.3 Rounding
+   ISO C 23 § 7.6.5 Rounding  */
+
+#if !@HAVE_FENV_H@
+
+# if (defined __x86_64__ || defined _M_X64) || (defined __i386 || defined _M_IX86)
+
+/* Attention: MSVC has different values for these macros!  */
+#  define FE_TONEAREST   (0 << 10)
+#  define FE_DOWNWARD    (1 << 10)
+#  define FE_UPWARD      (2 << 10)
+#  define FE_TOWARDZERO  (3 << 10)
+
+# elif defined __aarch64__ /* arm64 */
+
+#  define FE_TONEAREST   (0 << 22)
+#  define FE_UPWARD      (1 << 22)
+#  define FE_DOWNWARD    (2 << 22)
+#  define FE_TOWARDZERO  (3 << 22)
+
+# elif defined __arm__
+
+#  define FE_TONEAREST   (0 << 22)
+#  if !defined __SOFTFP__
+#   define FE_UPWARD      (1 << 22)
+#   define FE_DOWNWARD    (2 << 22)
+#   define FE_TOWARDZERO  (3 << 22)
+#  endif
+
+# elif defined __alpha
+
+#  define FE_TOWARDZERO  0
+#  define FE_DOWNWARD    1
+#  define FE_TONEAREST   2
+#  define FE_UPWARD      3
+
+# elif defined __hppa
+
+#  define FE_TONEAREST   (0 << 9)
+#  define FE_TOWARDZERO  (1 << 9)
+#  define FE_UPWARD      (2 << 9)
+#  define FE_DOWNWARD    (3 << 9)
+
+# elif defined __ia64__
+
+#  define FE_TONEAREST   0
+#  define FE_DOWNWARD    1
+#  define FE_UPWARD      2
+#  define FE_TOWARDZERO  3
+
+# elif defined __m68k__
+
+#  define FE_TONEAREST   (0 << 4)
+#  define FE_TOWARDZERO  (1 << 4)
+#  define FE_DOWNWARD    (2 << 4)
+#  define FE_UPWARD      (3 << 4)
+
+# elif defined __mips__
+
+#  define FE_TONEAREST   0
+#  define FE_TOWARDZERO  1
+#  define FE_UPWARD      2
+#  define FE_DOWNWARD    3
+
+# elif defined __loongarch__
+
+#  define FE_TONEAREST   (0 << 8)
+#  define FE_TOWARDZERO  (1 << 8)
+#  define FE_UPWARD      (2 << 8)
+#  define FE_DOWNWARD    (3 << 8)
+
+# elif defined __powerpc__
+
+/* Attention: AIX has different values for these macros!  */
+#  define FE_TONEAREST   0
+#  define FE_TOWARDZERO  1
+#  define FE_UPWARD      2
+#  define FE_DOWNWARD    3
+
+# elif defined __riscv
+
+#  define FE_TONEAREST   0
+#  define FE_TOWARDZERO  1
+#  define FE_DOWNWARD    2
+#  define FE_UPWARD      3
+#  if 0 /* non-standard */
+#   define FE_TONEARESTMAXMAGNITUDE 4
+#  endif
+
+# elif defined __s390__ || defined __s390x__
+
+#  define FE_TONEAREST   0
+#  define FE_TOWARDZERO  1
+#  define FE_UPWARD      2
+#  define FE_DOWNWARD    3
+
+# elif defined __sh__
+
+#  define FE_TONEAREST   0
+#  define FE_TOWARDZERO  1
+
+# elif defined __sparc
+
+/* Attention: FreeBSD libc has these values shifted right by 30 bits!  */
+#  define FE_TONEAREST   (0U << 30)
+#  define FE_TOWARDZERO  (1U << 30)
+#  define FE_UPWARD      (2U << 30)
+#  define FE_DOWNWARD    (3U << 30)
+
+#else
+
+/* A dummy fallback.  */
+
+#  define FE_TONEAREST   0
+
+# endif
+
+#endif
+
+
+/* ISO C 99 § 7.6.2 Floating-point exceptions
+   ISO C 23 § 7.6.4 Floating-point exceptions
+   API without fexcept_t  */
+
+#if !@HAVE_FENV_H@
+
+# if (defined __x86_64__ || defined _M_X64) || (defined __i386 || defined _M_IX86)
+
+/* Attention: MSVC has different values for these macros!  */
+#  define FE_INVALID    (1 << 0)
+#  if 0 /* non-standard */
+#   define FE_DENORMAL  (1 << 1)
+#  endif
+#  define FE_DIVBYZERO  (1 << 2)
+#  define FE_OVERFLOW   (1 << 3)
+#  define FE_UNDERFLOW  (1 << 4)
+#  define FE_INEXACT    (1 << 5)
+
+# elif defined __aarch64__ /* arm64 */ || defined __arm__
+
+#  define FE_INVALID    (1U << 0)
+#  define FE_DIVBYZERO  (1U << 1)
+#  define FE_OVERFLOW   (1U << 2)
+#  define FE_UNDERFLOW  (1U << 3)
+#  define FE_INEXACT    (1U << 4)
+
+# elif defined __alpha
+
+/* Attention: The *BSDs have these values shifted right by 17 bits!  */
+#  define FE_INVALID    (1UL << 17)
+#  define FE_DIVBYZERO  (1UL << 18)
+#  define FE_OVERFLOW   (1UL << 19)
+#  define FE_UNDERFLOW  (1UL << 20)
+#  define FE_INEXACT    (1UL << 21)
+#  if 0 /* non-standard */
+#   define FE_DENORMAL  (1UL << 22)
+#  endif
+
+# elif defined __hppa
+
+#  define FE_INEXACT    (1U << 0)
+#  define FE_UNDERFLOW  (1U << 1)
+#  define FE_OVERFLOW   (1U << 2)
+#  define FE_DIVBYZERO  (1U << 3)
+#  define FE_INVALID    (1U << 4)
+
+# elif defined __ia64__
+
+#  define FE_INVALID    (1UL << 0)
+#  if 0 /* non-standard */
+#   define FE_UNNORMAL  (1UL << 1)
+#  endif
+#  define FE_DIVBYZERO  (1UL << 2)
+#  define FE_OVERFLOW   (1UL << 3)
+#  define FE_UNDERFLOW  (1UL << 4)
+#  define FE_INEXACT    (1UL << 5)
+
+# elif defined __m68k__
+
+#  define FE_INEXACT    (1U << 3)
+#  define FE_DIVBYZERO  (1U << 4)
+#  define FE_UNDERFLOW  (1U << 5)
+#  define FE_OVERFLOW   (1U << 6)
+#  define FE_INVALID    (1U << 7)
+
+# elif defined __mips__
+
+#  define FE_INEXACT    (1 << 2)
+#  define FE_UNDERFLOW  (1 << 3)
+#  define FE_OVERFLOW   (1 << 4)
+#  define FE_DIVBYZERO  (1 << 5)
+#  define FE_INVALID    (1 << 6)
+
+# elif defined __loongarch__
+
+#  define FE_INEXACT    (1U << 16)
+#  define FE_UNDERFLOW  (1U << 17)
+#  define FE_OVERFLOW   (1U << 18)
+#  define FE_DIVBYZERO  (1U << 19)
+#  define FE_INVALID    (1U << 20)
+
+# elif defined __powerpc__
+
+#  define FE_INEXACT    (1U << 25)
+#  define FE_DIVBYZERO  (1U << 26)
+#  define FE_UNDERFLOW  (1U << 27)
+#  define FE_OVERFLOW   (1U << 28)
+#  define FE_INVALID    (1U << 29)
+
+# elif defined __riscv
+
+#  define FE_INEXACT    (1U << 0)
+#  define FE_UNDERFLOW  (1U << 1)
+#  define FE_OVERFLOW   (1U << 2)
+#  define FE_DIVBYZERO  (1U << 3)
+#  define FE_INVALID    (1U << 4)
+
+# elif defined __s390__ || defined __s390x__
+
+/* Attention: musl libc has these values shifted left by 16 bits!  */
+#  define FE_INEXACT    (1U << 3)
+#  define FE_UNDERFLOW  (1U << 4)
+#  define FE_OVERFLOW   (1U << 5)
+#  define FE_DIVBYZERO  (1U << 6)
+#  define FE_INVALID    (1U << 7)
+
+# elif defined __sh__
+
+#  define FE_INEXACT    (1 << 2)
+#  define FE_UNDERFLOW  (1 << 3)
+#  define FE_OVERFLOW   (1 << 4)
+#  define FE_DIVBYZERO  (1 << 5)
+#  define FE_INVALID    (1 << 6)
+
+# elif defined __sparc
+
+/* Attention: Solaris has these values shifted right by 5 bits!  */
+#  define FE_INEXACT    (1UL << 5)
+#  define FE_DIVBYZERO  (1UL << 6)
+#  define FE_UNDERFLOW  (1UL << 7)
+#  define FE_OVERFLOW   (1UL << 8)
+#  define FE_INVALID    (1UL << 9)
+
+# endif
+
+# define FE_ALL_EXCEPT \
+   (FE_DIVBYZERO | FE_INEXACT | FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW)
+
+#endif
+
+
+/* ISO C 99 § 7.6.2 Floating-point exceptions
+   ISO C 23 § 7.6.4 Floating-point exceptions
+   API with fexcept_t  */
+
+#if !@HAVE_FENV_H@
+
+# if (defined __x86_64__ || defined _M_X64) || (defined __i386 || defined _M_IX86)
+
+typedef unsigned short fexcept_t;
+
+# elif defined __aarch64__ /* arm64 */ || defined __arm__
+
+typedef unsigned int fexcept_t;
+
+# elif defined __alpha
+
+typedef unsigned long fexcept_t;
+
+# elif defined __hppa
+
+typedef unsigned int fexcept_t;
+
+# elif defined __ia64__
+
+typedef unsigned long fexcept_t;
+
+# elif defined __m68k__
+
+typedef unsigned int fexcept_t;
+
+# elif defined __mips__
+
+typedef unsigned short fexcept_t;
+
+# elif defined __loongarch__
+
+typedef unsigned int fexcept_t;
+
+# elif defined __powerpc__
+
+typedef unsigned int fexcept_t;
+
+# elif defined __riscv
+
+typedef unsigned int fexcept_t;
+
+# elif defined __s390__ || defined __s390x__
+
+typedef unsigned int fexcept_t;
+
+# elif defined __sh__
+
+typedef unsigned short fexcept_t;
+
+# elif defined __sparc
+
+typedef unsigned long fexcept_t;
+
+# else
+
+/* A dummy fallback.  */
+
+typedef unsigned long fexcept_t;
+
+# endif
+
+#endif
+
+
+#endif /* _@GUARD_PREFIX@_FENV_H */
+#endif /* _@GUARD_PREFIX@_FENV_H */
diff --git a/m4/fenv_h.m4 b/m4/fenv_h.m4
new file mode 100644
index 0000000000..9e7c809d06
--- /dev/null
+++ b/m4/fenv_h.m4
@@ -0,0 +1,98 @@
+# fenv_h.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_H],
+[
+  dnl Ensure to expand the default settings once only, before all statements
+  dnl that occur in other macros.
+  AC_REQUIRE([gl_FENV_H_DEFAULTS])
+
+  gl_CHECK_NEXT_HEADERS([fenv.h])
+  if test $ac_cv_header_fenv_h = yes; then
+    HAVE_FENV_H=1
+  else
+    HAVE_FENV_H=0
+  fi
+  AC_SUBST([HAVE_FENV_H])
+
+  dnl Check for declarations of anything we want to poison if the
+  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])
+])
+
+# gl_FENV_MODULE_INDICATOR([modulename])
+# sets the shell variable that indicates the presence of the given module
+# to a C preprocessor expression that will evaluate to 1.
+# This macro invocation must not occur in macros that are AC_REQUIREd.
+AC_DEFUN([gl_FENV_MODULE_INDICATOR],
+[
+  dnl Ensure to expand the default settings once only.
+  gl_FENV_H_REQUIRE_DEFAULTS
+  gl_MODULE_INDICATOR_SET_VARIABLE([$1])
+  dnl Define it also as a C macro, for the benefit of the unit tests.
+  gl_MODULE_INDICATOR_FOR_TESTS([$1])
+])
+
+# Initializes the default values for AC_SUBSTed shell variables.
+# This macro must not be AC_REQUIREd.  It must only be invoked, and only
+# outside of macros or in macros that are not AC_REQUIREd.
+AC_DEFUN([gl_FENV_H_REQUIRE_DEFAULTS],
+[
+  m4_defun(GL_MODULE_INDICATOR_PREFIX[_FENV_H_MODULE_INDICATOR_DEFAULTS], [
+    gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FECLEAREXCEPT])
+    gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FEDISABLEEXCEPT])
+    gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FEENABLEEXCEPT])
+    gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FEGETENV])
+    gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FEGETEXCEPT])
+    gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FEGETEXCEPTFLAG])
+    gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FEGETROUND])
+    gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FEHOLDEXCEPT])
+    gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FERAISEEXCEPT])
+    gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FESETENV])
+    gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FESETEXCEPT])
+    gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FESETEXCEPTFLAG])
+    gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FESETROUND])
+    gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FETESTEXCEPT])
+    gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FETESTEXCEPTFLAG])
+    gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FEUPDATEENV])
+  ])
+  m4_require(GL_MODULE_INDICATOR_PREFIX[_FENV_H_MODULE_INDICATOR_DEFAULTS])
+  AC_REQUIRE([gl_FENV_H_DEFAULTS])
+])
+
+AC_DEFUN([gl_FENV_H_DEFAULTS],
+[
+  dnl Assume proper GNU behavior unless another module says otherwise.
+  HAVE_FECLEAREXCEPT=1;              AC_SUBST([HAVE_FECLEAREXCEPT])
+  HAVE_FEDISABLEEXCEPT=1;            AC_SUBST([HAVE_FEDISABLEEXCEPT])
+  HAVE_FEENABLEEXCEPT=1;             AC_SUBST([HAVE_FEENABLEEXCEPT])
+  HAVE_FEGETENV=1;                   AC_SUBST([HAVE_FEGETENV])
+  HAVE_FEGETEXCEPT=1;                AC_SUBST([HAVE_FEGETEXCEPT])
+  HAVE_FEGETEXCEPTFLAG=1;            AC_SUBST([HAVE_FEGETEXCEPTFLAG])
+  HAVE_FEGETROUND=1;                 AC_SUBST([HAVE_FEGETROUND])
+  HAVE_FEHOLDEXCEPT=1;               AC_SUBST([HAVE_FEHOLDEXCEPT])
+  HAVE_FERAISEEXCEPT=1;              AC_SUBST([HAVE_FERAISEEXCEPT])
+  HAVE_FESETENV=1;                   AC_SUBST([HAVE_FESETENV])
+  HAVE_FESETEXCEPT=1;                AC_SUBST([HAVE_FESETEXCEPT])
+  HAVE_FESETEXCEPTFLAG=1;            AC_SUBST([HAVE_FESETEXCEPTFLAG])
+  HAVE_FESETROUND=1;                 AC_SUBST([HAVE_FESETROUND])
+  HAVE_FETESTEXCEPT=1;               AC_SUBST([HAVE_FETESTEXCEPT])
+  HAVE_FETESTEXCEPTFLAG=1;           AC_SUBST([HAVE_FETESTEXCEPTFLAG])
+  HAVE_FEUPDATEENV=1;                AC_SUBST([HAVE_FEUPDATEENV])
+  REPLACE_FECLEAREXCEPT=0;           AC_SUBST([REPLACE_FECLEAREXCEPT])
+  REPLACE_FEDISABLEEXCEPT=0;         AC_SUBST([REPLACE_FEDISABLEEXCEPT])
+  REPLACE_FEENABLEEXCEPT=0;          AC_SUBST([REPLACE_FEENABLEEXCEPT])
+  REPLACE_FEGETEXCEPT=0;             AC_SUBST([REPLACE_FEGETEXCEPT])
+  REPLACE_FEGETEXCEPTFLAG=0;         AC_SUBST([REPLACE_FEGETEXCEPTFLAG])
+  REPLACE_FEGETROUND=0;              AC_SUBST([REPLACE_FEGETROUND])
+  REPLACE_FERAISEEXCEPT=0;           AC_SUBST([REPLACE_FERAISEEXCEPT])
+  REPLACE_FESETEXCEPT=0;             AC_SUBST([REPLACE_FESETEXCEPT])
+  REPLACE_FESETEXCEPTFLAG=0;         AC_SUBST([REPLACE_FESETEXCEPTFLAG])
+  REPLACE_FESETROUND=0;              AC_SUBST([REPLACE_FESETROUND])
+  REPLACE_FETESTEXCEPT=0;            AC_SUBST([REPLACE_FETESTEXCEPT])
+])
diff --git a/modules/fenv b/modules/fenv
new file mode 100644
index 0000000000..270db4bd27
--- /dev/null
+++ b/modules/fenv
@@ -0,0 +1,87 @@
+Description:
+A GNU-like <fenv.h>.
+It nearly conforms to C99 and to C23.
+
+Files:
+lib/fenv.in.h
+m4/fenv_h.m4
+
+Depends-on:
+gen-header
+include_next
+snippet/warn-on-use
+
+configure.ac:
+gl_FENV_H
+gl_FENV_H_REQUIRE_DEFAULTS
+AC_PROG_MKDIR_P
+
+Makefile.am:
+BUILT_SOURCES += fenv.h
+
+# We need the following in order to create an override of <fenv.h>.
+fenv.h: fenv.in.h $(top_builddir)/config.status $(WARN_ON_USE_H)
+@NMD@	$(AM_V_GEN)$(MKDIR_P) '%reldir%'
+	$(gl_V_at)$(SED_HEADER_STDOUT) \
+	      -e 's|@''GUARD_PREFIX''@|${gl_include_guard_prefix}|g' \
+	      -e 's/@''HAVE_FENV_H''@/$(HAVE_FENV_H)/g' \
+	      -e 's|@''INCLUDE_NEXT''@|$(INCLUDE_NEXT)|g' \
+	      -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \
+	      -e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \
+	      -e 's|@''NEXT_FENV_H''@|$(NEXT_FENV_H)|g' \
+	      -e 's/@''GNULIB_FECLEAREXCEPT''@/$(GNULIB_FECLEAREXCEPT)/g' \
+	      -e 's/@''GNULIB_FEDISABLEEXCEPT''@/$(GNULIB_FEDISABLEEXCEPT)/g' \
+	      -e 's/@''GNULIB_FEENABLEEXCEPT''@/$(GNULIB_FEENABLEEXCEPT)/g' \
+	      -e 's/@''GNULIB_FEGETENV''@/$(GNULIB_FEGETENV)/g' \
+	      -e 's/@''GNULIB_FEGETEXCEPT''@/$(GNULIB_FEGETEXCEPT)/g' \
+	      -e 's/@''GNULIB_FEGETEXCEPTFLAG''@/$(GNULIB_FEGETEXCEPTFLAG)/g' \
+	      -e 's/@''GNULIB_FEGETROUND''@/$(GNULIB_FEGETROUND)/g' \
+	      -e 's/@''GNULIB_FEHOLDEXCEPT''@/$(GNULIB_FEHOLDEXCEPT)/g' \
+	      -e 's/@''GNULIB_FERAISEEXCEPT''@/$(GNULIB_FERAISEEXCEPT)/g' \
+	      -e 's/@''GNULIB_FESETENV''@/$(GNULIB_FESETENV)/g' \
+	      -e 's/@''GNULIB_FESETEXCEPT''@/$(GNULIB_FESETEXCEPT)/g' \
+	      -e 's/@''GNULIB_FESETEXCEPTFLAG''@/$(GNULIB_FESETEXCEPTFLAG)/g' \
+	      -e 's/@''GNULIB_FESETROUND''@/$(GNULIB_FESETROUND)/g' \
+	      -e 's/@''GNULIB_FETESTEXCEPT''@/$(GNULIB_FETESTEXCEPT)/g' \
+	      -e 's/@''GNULIB_FETESTEXCEPTFLAG''@/$(GNULIB_FETESTEXCEPTFLAG)/g' \
+	      -e 's/@''GNULIB_FEUPDATEENV''@/$(GNULIB_FEUPDATEENV)/g' \
+	      -e 's|@''HAVE_FECLEAREXCEPT''@|$(HAVE_FECLEAREXCEPT)|g' \
+	      -e 's|@''HAVE_FEDISABLEEXCEPT''@|$(HAVE_FEDISABLEEXCEPT)|g' \
+	      -e 's|@''HAVE_FEENABLEEXCEPT''@|$(HAVE_FEENABLEEXCEPT)|g' \
+	      -e 's|@''HAVE_FEGETENV''@|$(HAVE_FEGETENV)|g' \
+	      -e 's|@''HAVE_FEGETEXCEPT''@|$(HAVE_FEGETEXCEPT)|g' \
+	      -e 's|@''HAVE_FEGETEXCEPTFLAG''@|$(HAVE_FEGETEXCEPTFLAG)|g' \
+	      -e 's|@''HAVE_FEGETROUND''@|$(HAVE_FEGETROUND)|g' \
+	      -e 's|@''HAVE_FEHOLDEXCEPT''@|$(HAVE_FEHOLDEXCEPT)|g' \
+	      -e 's|@''HAVE_FERAISEEXCEPT''@|$(HAVE_FERAISEEXCEPT)|g' \
+	      -e 's|@''HAVE_FESETENV''@|$(HAVE_FESETENV)|g' \
+	      -e 's|@''HAVE_FESETEXCEPT''@|$(HAVE_FESETEXCEPT)|g' \
+	      -e 's|@''HAVE_FESETEXCEPTFLAG''@|$(HAVE_FESETEXCEPTFLAG)|g' \
+	      -e 's|@''HAVE_FESETROUND''@|$(HAVE_FESETROUND)|g' \
+	      -e 's|@''HAVE_FETESTEXCEPT''@|$(HAVE_FETESTEXCEPT)|g' \
+	      -e 's|@''HAVE_FETESTEXCEPTFLAG''@|$(HAVE_FETESTEXCEPTFLAG)|g' \
+	      -e 's|@''HAVE_FEUPDATEENV''@|$(HAVE_FEUPDATEENV)|g' \
+	      -e 's|@''REPLACE_FECLEAREXCEPT''@|$(REPLACE_FECLEAREXCEPT)|g' \
+	      -e 's|@''REPLACE_FEDISABLEEXCEPT''@|$(REPLACE_FEDISABLEEXCEPT)|g' \
+	      -e 's|@''REPLACE_FEENABLEEXCEPT''@|$(REPLACE_FEENABLEEXCEPT)|g' \
+	      -e 's|@''REPLACE_FEGETEXCEPT''@|$(REPLACE_FEGETEXCEPT)|g' \
+	      -e 's|@''REPLACE_FEGETEXCEPTFLAG''@|$(REPLACE_FEGETEXCEPTFLAG)|g' \
+	      -e 's|@''REPLACE_FEGETROUND''@|$(REPLACE_FEGETROUND)|g' \
+	      -e 's|@''REPLACE_FERAISEEXCEPT''@|$(REPLACE_FERAISEEXCEPT)|g' \
+	      -e 's|@''REPLACE_FESETEXCEPT''@|$(REPLACE_FESETEXCEPT)|g' \
+	      -e 's|@''REPLACE_FESETEXCEPTFLAG''@|$(REPLACE_FESETEXCEPTFLAG)|g' \
+	      -e 's|@''REPLACE_FESETROUND''@|$(REPLACE_FESETROUND)|g' \
+	      -e 's|@''REPLACE_FETESTEXCEPT''@|$(REPLACE_FETESTEXCEPT)|g' \
+	      -e '/definition of _GL_WARN_ON_USE/r $(WARN_ON_USE_H)' \
+	      $(srcdir)/fenv.in.h > $@-t
+	$(AM_V_at)mv $@-t $@
+MOSTLYCLEANFILES += fenv.h fenv.h-t
+
+Include:
+<fenv.h>
+
+License:
+LGPLv2+
+
+Maintainer:
+all
-- 
2.34.1

>From 3aac95314ccc57d53380e8c69922a6d33203bc5f Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Fri, 27 Oct 2023 03:46:25 +0200
Subject: [PATCH 2/2] fenv: Add tests.

* tests/test-fenv.c: New file.
* modules/fenv-tests: New file.
---
 ChangeLog          |  4 +++
 modules/fenv-tests | 11 ++++++++
 tests/test-fenv.c  | 69 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 84 insertions(+)
 create mode 100644 modules/fenv-tests
 create mode 100644 tests/test-fenv.c

diff --git a/ChangeLog b/ChangeLog
index b89af7a969..82e7021279 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
 2023-10-26  Bruno Haible  <br...@clisp.org>
 
+	fenv: Add tests.
+	* tests/test-fenv.c: New file.
+	* modules/fenv-tests: New file.
+
 	fenv: New module.
 	* lib/fenv.in.h: New file, based on glibc.
 	* m4/fenv_h.m4: New file.
diff --git a/modules/fenv-tests b/modules/fenv-tests
new file mode 100644
index 0000000000..ec9048e202
--- /dev/null
+++ b/modules/fenv-tests
@@ -0,0 +1,11 @@
+Files:
+tests/test-fenv.c
+
+Depends-on:
+verify
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-fenv
+check_PROGRAMS += test-fenv
diff --git a/tests/test-fenv.c b/tests/test-fenv.c
new file mode 100644
index 0000000000..e0911b7839
--- /dev/null
+++ b/tests/test-fenv.c
@@ -0,0 +1,69 @@
+/* Test of <fenv.h> substitute.
+   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 "verify.h"
+
+/* Check that the various FE_* macros are defined.  */
+int r[] =
+  {
+#ifdef FE_DOWNWARD
+    FE_DOWNWARD,
+#endif
+#ifdef FE_UPWARD
+    FE_UPWARD,
+#endif
+#ifdef FE_TOWARDZERO
+    FE_TOWARDZERO,
+#endif
+    FE_TONEAREST
+  };
+int e[] = { FE_DIVBYZERO, FE_INEXACT, FE_INVALID, FE_OVERFLOW, FE_UNDERFLOW };
+
+/* Check that the types are all defined.  */
+fenv_t t1;
+fexcept_t t2;
+
+/* On many platforms, other FE_* constants are included in FE_ALL_EXCEPT,
+   therefore in general
+     FE_ALL_EXCEPT == (FE_DIVBYZERO | FE_INEXACT | FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW)
+   does not hold.  */
+verify (((FE_DIVBYZERO | FE_INEXACT | FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW)
+         & ~FE_ALL_EXCEPT)
+        == 0);
+
+int
+main (void)
+{
+  /* Ensure no overlap in FE_*. */
+  switch (FE_INVALID)
+    {
+    case FE_DIVBYZERO:
+    case FE_INEXACT:
+    case FE_INVALID:
+    case FE_OVERFLOW:
+    case FE_UNDERFLOW:
+      ;
+    }
+
+  return 0;
+}
-- 
2.34.1

Reply via email to