This patch updates the 'float' module, to enable the macros that were added
in ISO C 11 and ISO C 23.

Unfortunately, we cannot make things completely ISO C compliant, regarding
  - The requirement that some macros must expand to constant expressions.
    There is no way to make e.g. LDBL_SNAN be a constant expression without
    compiler support.
  - The FLT_IS_IEC_60559, DBL_IS_IEC_60559, LDBL_IS_IEC_60559 macros. The
    standard is just too vague regarding the meaning of these macros.


2024-07-31  Bruno Haible  <br...@clisp.org>

        float: Update to mostly guarantee ISO C 23 compliance.
        * doc/posix-headers/float.texi: Mention the various portability
        problems.
        * lib/float.in.h (FLT_HAS_SUBNORM, FLT_DECIMAL_DIG, FLT_TRUE_MIN,
        DBL_HAS_SUBNORM, DBL_DECIMAL_DIG, DBL_TRUE_MIN, LDBL_HAS_SUBNORM,
        LDBL_DECIMAL_DIG, LDBL_TRUE_MIN): New macros.
        (gl_LDBL_TRUE_MIN): New declaration.
        (FLT_IS_IEC_60559, FLT_NORM_MAX, FLT_SNAN, GNULIB_defined_FLT_SNAN,
        DBL_IS_IEC_60559, DBL_NORM_MAX, DBL_SNAN, GNULIB_defined_DBL_SNAN,
        LDBL_IS_IEC_60559, LDBL_NORM_MAX, LDBL_SNAN, GNULIB_defined_LDBL_SNAN):
        New macros.
        (gl_FLT_SNAN_t, gl_DBL_SNAN_t, gl_LDBL_SNAN_t): New types.
        (gl_FLT_SNAN, gl_DBL_SNAN, gl_LDBL_SNAN): New declarations.
        * lib/float.c (gl_LDBL_TRUE_MIN): New variable.
        (gl_FLT_SNAN, gl_DBL_SNAN, gl_LDBL_SNAN): New variables.
        * m4/float_h.m4 (gl_FLOAT_H): Add check whether float.h conforms to
        ISO C23. Set REPLACE_FLOAT_SNAN.
        * modules/float (configure.ac): Test also REPLACE_FLOAT_SNAN. Require
        gl_BIGENDIAN.
        * tests/test-float.c: Also check FLT_EVAL_METHOD, FLT_HAS_SUBNORM,
        FLT_DECIMAL_DIG, FLT_IS_IEC_60559, FLT_TRUE_MIN, FLT_NORM_MAX,
        DBL_HAS_SUBNORM, DBL_DECIMAL_DIG, DBL_IS_IEC_60559, DBL_TRUE_MIN,
        DBL_NORM_MAX, LDBL_HAS_SUBNORM, LDBL_DECIMAL_DIG, LDBL_IS_IEC_60559.
        Conditionally check LDBL_TRUE_MIN, LDBL_NORM_MAX.
        Include isnanf-nolibm.h, isnand-nolibm.h, isnanl-nolibm.h.
        (test_float): Check the values of FLT_HAS_SUBNORM, FLT_DECIMAL_DIG,
        FLT_TRUE_MIN, FLT_IS_IEC_60559, FLT_NORM_MAX, FLT_SNAN.
        (test_double): Check the values of DBL_HAS_SUBNORM, DBL_DECIMAL_DIG,
        DBL_TRUE_MIN, DBL_IS_IEC_60559, DBL_NORM_MAX, DBL_SNAN.
        (test_long_double): Check the values of LDBL_HAS_SUBNORM,
        LDBL_DECIMAL_DIG, LDBL_TRUE_MIN, LDBL_IS_IEC_60559, LDBL_NORM_MAX,
        LDBL_SNAN.
        * modules/float-tests (Depends-on): Add isnanf-nolibm, isnand-nolibm,
        isnanl-nolibm.

diff --git a/doc/posix-headers/float.texi b/doc/posix-headers/float.texi
index 8e01932582..0dc41d7890 100644
--- a/doc/posix-headers/float.texi
+++ b/doc/posix-headers/float.texi
@@ -21,6 +21,26 @@
 On Linux/PowerPC with GCC 4.4, and on AIX 7.1 with GCC 4.2,
 they don't reflect the ``double double'' representation of @code{long double}
 correctly.
+@item
+The macros
+@code{FLT_HAS_SUBNORM}, @code{FLT_DECIMAL_DIG}, @code{FLT_TRUE_MIN},
+@code{DBL_HAS_SUBNORM}, @code{DBL_DECIMAL_DIG}, @code{DBL_TRUE_MIN},
+@code{LDBL_HAS_SUBNORM}, @code{LDBL_DECIMAL_DIG}, @code{LDBL_TRUE_MIN},
+that were added in ISO C 11, are missing on some platforms:
+gcc 4.5.4, clang 7, Solaris 11.3 with Sun C.
+@item
+The macro @code{LDBL_DECIMAL_DIG} is missing on some platforms:
+MSVC 14.
+@item
+The value of macro @code{FLT_TRUE_MIN} is not a @code{float} on some platforms:
+AIX 7.1 with xlc.
+@item
+The macros
+@code{FLT_IS_IEC_60559}, @code{FLT_NORM_MAX}, @code{FLT_SNAN},
+@code{DBL_IS_IEC_60559}, @code{DBL_NORM_MAX}, @code{DBL_SNAN},
+@code{LDBL_IS_IEC_60559}, @code{LDBL_NORM_MAX}, @code{LDBL_SNAN},
+that were added in ISO C 23, are missing on some platforms:
+gcc 14 without @code{-std=c23} or @code{-std=gnu23}, clang 18, musl libc 
1.2.5, AIX 7.3 with xlclang, Solaris 11.3 with Sun C, MSVC 14.
 @end itemize
 
 Portability problems not fixed by Gnulib:
@@ -29,4 +49,11 @@
 The macro @code{FLT_ROUNDS} is a constant expression and does not represent
 the current rounding mode on some platforms:
 glibc 2.11, HP-UX 11, mingw.
+@item
+The value of @code{FLT_TRUE_MIN} is not a constant on some platforms:
+FreeBSD/x86.
+@item
+The values of @code{FLT_SNAN}, @code{DBL_SNAN}, @code{LDBL_SNAN} are not
+constants on some platforms:
+AIX 7.3 with xlclang, and all platforms which use the gnulib replacements.
 @end itemize
diff --git a/lib/float.c b/lib/float.c
index a9ea40b063..7a563048e2 100644
--- a/lib/float.c
+++ b/lib/float.c
@@ -20,14 +20,101 @@
 /* Specification.  */
 #include <float.h>
 
-#if (defined _ARCH_PPC || defined _POWER) && (defined _AIX || defined 
__linux__) && (LDBL_MANT_DIG == 106) && defined __GNUC__
+#if GNULIB_defined_long_double_union
+# if (defined _ARCH_PPC || defined _POWER) && (defined _AIX || defined 
__linux__) && (LDBL_MANT_DIG == 106) && defined __GNUC__
 const union gl_long_double_union gl_LDBL_MAX =
   { { DBL_MAX, DBL_MAX / (double)134217728UL / (double)134217728UL } };
-#elif defined __i386__
+# elif defined __i386__
 const union gl_long_double_union gl_LDBL_MAX =
   { { 0xFFFFFFFF, 0xFFFFFFFF, 32766 } };
-#else
+# endif
+# if defined __i386__ && (defined __FreeBSD__ || defined __DragonFly__)
+/* We can't even simply evaluate the formula (LDBL_MIN / 
9223372036854775808.0L)
+   at run time, because it would require BEGIN_LONG_DOUBLE_ROUNDING /
+   END_LONG_DOUBLE_ROUNDING invocations.  It simpler to just write down the
+   representation of LDBL_TRUE_MIN, based on
+   
<https://en.wikipedia.org/wiki/Extended_precision#x86_extended_precision_format>.
  */
+const union gl_long_double_union gl_LDBL_TRUE_MIN =
+  { { 0x00000001, 0x00000000, 0 } };
+# endif
+#endif
+
+#if GNULIB_defined_FLT_SNAN
+/* Define like memory_positive_SNaNf(), see signed-snan.h and snan.h,
+   or like setpayloadsigf() with an arbitrary payload.  */
+gl_FLT_SNAN_t gl_FLT_SNAN =
+# if FLT_MANT_DIG == 24
+#  if defined __hppa || (defined __mips__ && !MIPS_NAN2008_FLOAT) || defined 
__sh__
+  /* sign bit: 0, 8 exponent bits: all 1, next bit: 1, payload: 0b10...0 */
+  { { 0x7FE00000U } }
+#  else
+  /* sign bit: 0, 8 exponent bits: all 1, next bit: 0, payload: 0b10...0 */
+  { { 0x7FA00000U } }
+#  endif
+# endif
+  ;
+#endif
+
+#if GNULIB_defined_DBL_SNAN
+/* Define like memory_positive_SNaNd(), see signed-snan.h and snan.h,
+   or like setpayloadsig() with an arbitrary payload.  */
+gl_DBL_SNAN_t gl_DBL_SNAN =
+# if DBL_MANT_DIG == 53
+#  if defined __hppa || (defined __mips__ && !MIPS_NAN2008_FLOAT) || defined 
__sh__
+  /* sign bit: 0, 11 exponent bits: all 1, next bit: 1, payload: 0b10...0 */
+  { { 0x7FFC000000000000ULL } }
+#  else
+  /* sign bit: 0, 11 exponent bits: all 1, next bit: 0, payload: 0b10...0 */
+  { { 0x7FF4000000000000ULL } }
+#  endif
+# endif
+  ;
+#endif
+
+#if GNULIB_defined_LDBL_SNAN
+# ifdef WORDS_BIGENDIAN
+#  define TWO(hi,lo) { hi, lo }
+# else
+#  define TWO(hi,lo) { lo, hi }
+# endif
+/* Define like memory_positive_SNaNl(), see signed-snan.h and snan.h,
+   or like setpayloadsigl() with an arbitrary payload.  */
+gl_LDBL_SNAN_t gl_LDBL_SNAN =
+# if LDBL_MANT_DIG == 53 /* on arm, hppa, mips, sh, but also MSVC */
+#  if defined __hppa || (defined __mips__ && !MIPS_NAN2008_FLOAT) || defined 
__sh__
+  /* sign bit: 0, 11 exponent bits: all 1, next bit: 1, payload: 0b10...0 */
+  { { 0x7FFC000000000000ULL } }
+#  else
+  /* sign bit: 0, 11 exponent bits: all 1, next bit: 0, payload: 0b10...0 */
+  { { 0x7FF4000000000000ULL } }
+#  endif
+# elif LDBL_MANT_DIG == 64 /* on i386, x86_64, ia64, m68k */
+#  if defined __m68k__
+  /* sign bit: 0, 15 exponent bits: all 1, 16 gap bits: all 0,
+     always=1 bit: 1, next bit: 0, payload: 0b10...0 */
+  { { 0x7FFF0000ULL, 0xA0000000ULL, 0x00000000ULL } }
+#  else
+  /* sign bit: 0, 15 exponent bits: all 1, always=1 bit: 1, next bit: 0, 
payload: 0b10...0
+     (see 
<https://en.wikipedia.org/wiki/Extended_precision#x86_extended_precision_format>)
 */
+  { TWO (0x00007FFFULL, 0xA000000000000000ULL) }
+#  endif
+# elif LDBL_MANT_DIG == 106 /* on powerpc, powerpc64, powerpc64le */
+  /* most-significant double:
+     sign bit: 0, 11 exponent bits: all 1, next bit: 0, payload: 0b10...0,
+     least-significant double: 0.0 */
+  { { 0x7FF4000000000000ULL, 0ULL } }
+# elif LDBL_MANT_DIG == 113 /* on alpha, arm64, loongarch64, mips64, riscv64, 
s390x, sparc64 */
+#  if (defined __mips__ && !MIPS_NAN2008_FLOAT)
+  /* sign bit: 0, 15 exponent bits: all 1, next bit: 1, payload: 0b10...0 */
+  { TWO (0x7FFFC00000000000ULL, 0ULL) }
+#  else
+  /* sign bit: 0, 15 exponent bits: all 1, next bit: 0, payload: 0b10...0 */
+  { TWO (0x7FFF400000000000ULL, 0ULL) }
+#  endif
+# endif
+  ;
+#endif
+
 /* This declaration is solely to ensure that after preprocessing
    this file is never empty.  */
 typedef int dummy;
-#endif
diff --git a/lib/float.in.h b/lib/float.in.h
index 73e8d40621..5e78b4f296 100644
--- a/lib/float.in.h
+++ b/lib/float.in.h
@@ -28,6 +28,8 @@
 #ifndef _@GUARD_PREFIX@_FLOAT_H
 #define _@GUARD_PREFIX@_FLOAT_H
 
+/* ============================ ISO C99 support ============================ */
+
 /* 'long double' properties.  */
 
 #if defined __i386__ && (defined __BEOS__ || defined __OpenBSD__)
@@ -179,6 +181,156 @@ extern const union gl_long_double_union gl_LDBL_MAX;
 # endif
 #endif
 
+/* ============================ ISO C11 support ============================ */
+
+/* 'float' properties */
+
+#ifndef FLT_HAS_SUBNORM
+# define FLT_HAS_SUBNORM 1
+#endif
+#ifndef FLT_DECIMAL_DIG
+/* FLT_MANT_DIG = 24 => FLT_DECIMAL_DIG = 9 */
+# define FLT_DECIMAL_DIG ((int)(FLT_MANT_DIG * 0.3010299956639812 + 2))
+#endif
+#if defined _AIX && !defined __GNUC__
+/* On AIX, the value of FLT_TRUE_MIN in /usr/include/float.h is a 'double',
+   not a 'float'.  */
+# undef FLT_TRUE_MIN
+#endif
+#ifndef FLT_TRUE_MIN
+/* FLT_MIN / 2^(FLT_MANT_DIG-1) */
+# define FLT_TRUE_MIN (FLT_MIN / 8388608.0f)
+#endif
+
+/* 'double' properties */
+
+#ifndef DBL_HAS_SUBNORM
+# define DBL_HAS_SUBNORM 1
+#endif
+#ifndef DBL_DECIMAL_DIG
+/* DBL_MANT_DIG = 53 => DBL_DECIMAL_DIG = 17 */
+# define DBL_DECIMAL_DIG ((int)(DBL_MANT_DIG * 0.3010299956639812 + 2))
+#endif
+#ifndef DBL_TRUE_MIN
+/* DBL_MIN / 2^(DBL_MANT_DIG-1) */
+# define DBL_TRUE_MIN (DBL_MIN / 4503599627370496.0)
+#endif
+
+/* 'long double' properties */
+
+#ifndef LDBL_HAS_SUBNORM
+# define LDBL_HAS_SUBNORM 1
+#endif
+#ifndef LDBL_DECIMAL_DIG
+/* LDBL_MANT_DIG = 53 => LDBL_DECIMAL_DIG = 17 */
+/* LDBL_MANT_DIG = 64 => LDBL_DECIMAL_DIG = 21 */
+/* LDBL_MANT_DIG = 106 => LDBL_DECIMAL_DIG = 33 */
+/* LDBL_MANT_DIG = 113 => LDBL_DECIMAL_DIG = 36 */
+# define LDBL_DECIMAL_DIG ((int)(LDBL_MANT_DIG * 0.3010299956639812 + 2))
+#endif
+#ifndef LDBL_TRUE_MIN
+/* LDBL_MIN / 2^(LDBL_MANT_DIG-1) */
+# if LDBL_MANT_DIG == 53
+#  define LDBL_TRUE_MIN (LDBL_MIN / 4503599627370496.0L)
+# elif LDBL_MANT_DIG == 64
+#  if defined __i386__ && (defined __FreeBSD__ || defined __DragonFly__)
+/* Work around FreeBSD/x86 problem mentioned above.  */
+extern const union gl_long_double_union gl_LDBL_TRUE_MIN;
+#   define LDBL_TRUE_MIN (gl_LDBL_TRUE_MIN.ld)
+#  else
+#   define LDBL_TRUE_MIN (LDBL_MIN / 9223372036854775808.0L)
+#  endif
+# elif LDBL_MANT_DIG == 106
+#  define LDBL_TRUE_MIN (LDBL_MIN / 40564819207303340847894502572032.0L)
+# elif LDBL_MANT_DIG == 113
+#  define LDBL_TRUE_MIN (LDBL_MIN / 5192296858534827628530496329220096.0L)
+# endif
+#endif
+
+/* ============================ ISO C23 support ============================ */
+
+/* 'float' properties */
+
+#ifndef FLT_IS_IEC_60559
+# if defined __m68k__
+#  define FLT_IS_IEC_60559 0
+# else
+#  define FLT_IS_IEC_60559 1
+# endif
+#endif
+#ifndef FLT_NORM_MAX
+# define FLT_NORM_MAX FLT_MAX
+#endif
+#ifndef FLT_SNAN
+/* For sh, beware of <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111814>.  */
+# if ((__GNUC__ + (__GNUC_MINOR__ >= 3) > 3) || defined __clang__) && !defined 
__sh__
+#  define FLT_SNAN __builtin_nansf ("")
+# else
+typedef union { unsigned int word[1]; float value; } gl_FLT_SNAN_t;
+extern gl_FLT_SNAN_t gl_FLT_SNAN;
+#  define FLT_SNAN (gl_FLT_SNAN.value)
+#  define GNULIB_defined_FLT_SNAN 1
+# endif
+#endif
+
+/* 'double' properties */
+
+#ifndef DBL_IS_IEC_60559
+# if defined __m68k__
+#  define DBL_IS_IEC_60559 0
+# else
+#  define DBL_IS_IEC_60559 1
+# endif
+#endif
+#ifndef DBL_NORM_MAX
+# define DBL_NORM_MAX DBL_MAX
+#endif
+#ifndef DBL_SNAN
+/* For sh, beware of <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111814>.  */
+# if ((__GNUC__ + (__GNUC_MINOR__ >= 3) > 3) || defined __clang__) && !defined 
__sh__
+#  define DBL_SNAN __builtin_nans ("")
+# else
+typedef union { unsigned long long word[1]; double value; } gl_DBL_SNAN_t;
+extern gl_DBL_SNAN_t gl_DBL_SNAN;
+#  define DBL_SNAN (gl_DBL_SNAN.value)
+#  define GNULIB_defined_DBL_SNAN 1
+# endif
+#endif
+
+/* 'long double' properties */
+
+#ifndef LDBL_IS_IEC_60559
+# if defined __m68k__
+#  define LDBL_IS_IEC_60559 0
+# elif LDBL_MANT_DIG == 53 || LDBL_MANT_DIG == 113
+#  define LDBL_IS_IEC_60559 1
+# else
+#  define LDBL_IS_IEC_60559 0
+# endif
+#endif
+#ifndef LDBL_NORM_MAX
+# define LDBL_NORM_MAX LDBL_MAX
+#endif
+#ifndef LDBL_SNAN
+/* For sh, beware of <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111814>.  */
+# if ((__GNUC__ + (__GNUC_MINOR__ >= 3) > 3) || defined __clang__) && !defined 
__sh__
+#  define LDBL_SNAN __builtin_nansl ("")
+# else
+#  if LDBL_MANT_DIG == 53
+typedef union { unsigned long long word[1]; long double value; } 
gl_LDBL_SNAN_t;
+#  elif defined __m68k__
+typedef union { unsigned int word[3]; long double value; } gl_LDBL_SNAN_t;
+#  else
+typedef union { unsigned long long word[2]; long double value; } 
gl_LDBL_SNAN_t;
+#  endif
+extern gl_LDBL_SNAN_t gl_LDBL_SNAN;
+#  define LDBL_SNAN (gl_LDBL_SNAN.value)
+#  define GNULIB_defined_LDBL_SNAN 1
+# endif
+#endif
+
+/* ================================= Other ================================= */
+
 #if @REPLACE_ITOLD@
 /* Pull in a function that fixes the 'int' to 'long double' conversion
    of glibc 2.7.  */
diff --git a/m4/float_h.m4 b/m4/float_h.m4
index c95d417161..12c0eb6749 100644
--- a/m4/float_h.m4
+++ b/m4/float_h.m4
@@ -1,5 +1,5 @@
 # float_h.m4
-# serial 14
+# serial 15
 dnl Copyright (C) 2007, 2009-2024 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -54,6 +54,31 @@ AC_DEFUN([gl_FLOAT_H]
       ;;
   esac
 
+  dnl Test for completeness w.r.t. ISO C 23.
+  REPLACE_FLOAT_SNAN=0
+  AC_CACHE_CHECK([whether float.h conforms to ISO C23],
+    [gl_cv_header_float_h_isoc23],
+    [AC_COMPILE_IFELSE(
+       [AC_LANG_PROGRAM(
+          [[#include <float.h>
+            int x[] = { FLT_DECIMAL_DIG, DBL_DECIMAL_DIG, LDBL_DECIMAL_DIG };
+            float maxf = FLT_NORM_MAX;
+            double maxd = DBL_NORM_MAX;
+            long double maxl = LDBL_NORM_MAX;
+          ]],
+          [[float sf = FLT_SNAN;
+            double sd = DBL_SNAN;
+            long double sl = LDBL_SNAN;
+            return (sf != 0) + (sd != 0) + (sl != 0);
+          ]])],
+       [gl_cv_header_float_h_isoc23=yes],
+       [gl_cv_header_float_h_isoc23=no])
+    ])
+  if test $gl_cv_header_float_h_isoc23 != yes; then
+    GL_GENERATE_FLOAT_H=true
+    REPLACE_FLOAT_SNAN=1
+  fi
+
   dnl Test against glibc-2.7 Linux/SPARC64 bug.
   REPLACE_ITOLD=0
   AC_CACHE_CHECK([whether conversion from 'int' to 'long double' works],
diff --git a/modules/float b/modules/float
index 600de5ebee..85c0571f18 100644
--- a/modules/float
+++ b/modules/float
@@ -15,8 +15,11 @@ configure.ac:
 gl_FLOAT_H
 gl_CONDITIONAL_HEADER([float.h])
 AC_PROG_MKDIR_P
-gl_CONDITIONAL([GL_COND_OBJ_FLOAT], [test $REPLACE_FLOAT_LDBL = 1])
+gl_CONDITIONAL([GL_COND_OBJ_FLOAT],
+               [test $REPLACE_FLOAT_LDBL = 1 || test $REPLACE_FLOAT_SNAN = 1])
 gl_CONDITIONAL([GL_COND_OBJ_ITOLD], [test $REPLACE_ITOLD = 1])
+dnl Prerequisites of lib/float.c.
+AC_REQUIRE([gl_BIGENDIAN])
 
 Makefile.am:
 BUILT_SOURCES += $(FLOAT_H)
diff --git a/modules/float-tests b/modules/float-tests
index a68e0216dd..7cb0870983 100644
--- a/modules/float-tests
+++ b/modules/float-tests
@@ -5,6 +5,9 @@ tests/macros.h
 Depends-on:
 fpieee
 fpucw
+isnanf-nolibm
+isnand-nolibm
+isnanl-nolibm
 float-c++-tests
 
 configure.ac:
diff --git a/tests/test-float.c b/tests/test-float.c
index c8006058e4..7614e32e07 100644
--- a/tests/test-float.c
+++ b/tests/test-float.c
@@ -20,8 +20,12 @@
 
 #include <float.h>
 
-/* Check that FLT_RADIX is a constant expression.  */
-int a[] = { FLT_RADIX };
+/* Check that some macros are constant expressions.  */
+int a[] =
+  {
+    FLT_RADIX,
+    FLT_EVAL_METHOD /* added in ISO C 99 */
+  };
 
 /* ----------------------- Check macros for 'float' ----------------------- */
 
@@ -29,9 +33,20 @@ int a[] = { FLT_RADIX };
 int fb[] =
   {
     FLT_MANT_DIG, FLT_MIN_EXP, FLT_MAX_EXP,
-    FLT_DIG, FLT_MIN_10_EXP, FLT_MAX_10_EXP
+    FLT_DIG, FLT_MIN_10_EXP, FLT_MAX_10_EXP,
+    FLT_HAS_SUBNORM, /* added in ISO C 11 */
+    FLT_DECIMAL_DIG, /* added in ISO C 11 */
+    FLT_IS_IEC_60559 /* added in ISO C 23 */
   };
-float fc[] = { FLT_EPSILON, FLT_MIN, FLT_MAX };
+float fc[] =
+  {
+    FLT_EPSILON, FLT_MIN, FLT_MAX,
+    FLT_TRUE_MIN, /* added in ISO C 11 */
+    FLT_NORM_MAX /* added in ISO C 23 */
+  };
+#if 0 /* FLT_SNAN is not a constant expression on some platforms.  */
+float fs = FLT_SNAN; /* added in ISO C 23 */
+#endif
 
 /* ----------------------- Check macros for 'double' ----------------------- */
 
@@ -39,9 +54,20 @@ float fc[] = { FLT_EPSILON, FLT_MIN, FLT_MAX };
 int db[] =
   {
     DBL_MANT_DIG, DBL_MIN_EXP, DBL_MAX_EXP,
-    DBL_DIG, DBL_MIN_10_EXP, DBL_MAX_10_EXP
+    DBL_DIG, DBL_MIN_10_EXP, DBL_MAX_10_EXP,
+    DBL_HAS_SUBNORM, /* added in ISO C 11 */
+    DBL_DECIMAL_DIG, /* added in ISO C 11 */
+    DBL_IS_IEC_60559 /* added in ISO C 23 */
   };
-double dc[] = { DBL_EPSILON, DBL_MIN, DBL_MAX };
+double dc[] =
+  {
+    DBL_EPSILON, DBL_MIN, DBL_MAX,
+    DBL_TRUE_MIN, /* added in ISO C 11 */
+    DBL_NORM_MAX /* added in ISO C 23 */
+  };
+#if 0 /* DBL_SNAN is not a constant expression on some platforms.  */
+double ds = DBL_SNAN; /* added in ISO C 23 */
+#endif
 
 /* -------------------- Check macros for 'long double' -------------------- */
 
@@ -49,17 +75,36 @@ double dc[] = { DBL_EPSILON, DBL_MIN, DBL_MAX };
 int lb[] =
   {
     LDBL_MANT_DIG, LDBL_MIN_EXP, LDBL_MAX_EXP,
-    LDBL_DIG, LDBL_MIN_10_EXP, LDBL_MAX_10_EXP
+    LDBL_DIG, LDBL_MIN_10_EXP, LDBL_MAX_10_EXP,
+    LDBL_HAS_SUBNORM, /* added in ISO C 11 */
+    LDBL_DECIMAL_DIG, /* added in ISO C 11 */
+    LDBL_IS_IEC_60559 /* added in ISO C 23 */
   };
 long double lc1 = LDBL_EPSILON;
 long double lc2 = LDBL_MIN;
-#if 0 /* LDBL_MAX is not a constant expression on some platforms.  */
+#if !GNULIB_defined_long_double_union
+/* LDBL_MAX is not a constant expression on some platforms.  */
 long double lc3 = LDBL_MAX;
 #endif
+#if !GNULIB_defined_long_double_union
+/* LDBL_TRUE_MIN is not a constant expression on FreeBSD/i386.  */
+long double lc4 = LDBL_TRUE_MIN; /* added in ISO C 11 */
+#endif
+#if !GNULIB_defined_long_double_union
+/* LDBL_MAX is not a constant expression on some platforms.  */
+long double lc5 = LDBL_NORM_MAX; /* added in ISO C 23 */
+#endif
+#if 0
+/* LDBL_SNAN is not a constant expression on some platforms.  */
+long double ls = LDBL_SNAN; /* added in ISO C 23 */
+#endif
 
 /* ------------------------------------------------------------------------- */
 
 #include "fpucw.h"
+#include "isnanf-nolibm.h"
+#include "isnand-nolibm.h"
+#include "isnanl-nolibm.h"
 #include "macros.h"
 
 #if FLT_RADIX == 2
@@ -220,6 +265,34 @@ test_float (void)
           ASSERT (x <= 1.0f);
       }
   }
+
+  /* Check the value of FLT_HAS_SUBNORM.  */
+  ASSERT (FLT_HAS_SUBNORM == 1);
+
+  /* Check the value of FLT_DECIMAL_DIG.  */
+  ASSERT (FLT_DECIMAL_DIG == (int)(FLT_MANT_DIG * 0.3010299956639812 + 2));
+
+  /* Check the value of FLT_TRUE_MIN.  */
+  ASSERT (FLT_TRUE_MIN > 0.0f);
+  {
+    volatile float x = FLT_TRUE_MIN * 0.5f;
+    ASSERT (x == 0.0f);
+  }
+
+  /* Check the value of FLT_IS_IEC_60559.  */
+#if !defined __m68k__
+  ASSERT (FLT_IS_IEC_60559);
+#elif 0
+  /* It is not clear what this macro actually means.  Cf.
+     
<http://mailman.oakapple.net/pipermail/cfp-interest/2023-April/002760.html> */
+  ASSERT (!FLT_IS_IEC_60559);
+#endif
+
+  /* Check the value of FLT_NORM_MAX.  */
+  ASSERT (FLT_NORM_MAX == FLT_MAX);
+
+  /* Check the value of FLT_SNAN.  */
+  ASSERT (isnanf (FLT_SNAN));
 }
 
 /* ----------------------- Check macros for 'double' ----------------------- */
@@ -291,6 +364,34 @@ test_double (void)
           ASSERT (x <= 1.0);
       }
   }
+
+  /* Check the value of DBL_HAS_SUBNORM.  */
+  ASSERT (DBL_HAS_SUBNORM == 1);
+
+  /* Check the value of DBL_DECIMAL_DIG.  */
+  ASSERT (DBL_DECIMAL_DIG == (int)(DBL_MANT_DIG * 0.3010299956639812 + 2));
+
+  /* Check the value of DBL_TRUE_MIN.  */
+  ASSERT (DBL_TRUE_MIN > 0.0);
+  {
+    volatile double x = DBL_TRUE_MIN * 0.5;
+    ASSERT (x == 0.0);
+  }
+
+  /* Check the value of DBL_IS_IEC_60559.  */
+#if !defined __m68k__
+  ASSERT (DBL_IS_IEC_60559);
+#elif 0
+  /* It is not clear what this macro actually means.  Cf.
+     
<http://mailman.oakapple.net/pipermail/cfp-interest/2023-April/002760.html> */
+  ASSERT (!DBL_IS_IEC_60559);
+#endif
+
+  /* Check the value of DBL_NORM_MAX.  */
+  ASSERT (DBL_NORM_MAX == DBL_MAX);
+
+  /* Check the value of DBL_SNAN.  */
+  ASSERT (isnand (DBL_SNAN));
 }
 
 /* -------------------- Check macros for 'long double' -------------------- */
@@ -359,6 +460,34 @@ test_long_double (void)
           ASSERT (x <= 1.0L);
       }
   }
+
+  /* Check the value of LDBL_HAS_SUBNORM.  */
+  ASSERT (LDBL_HAS_SUBNORM == 1);
+
+  /* Check the value of LDBL_DECIMAL_DIG.  */
+  ASSERT (LDBL_DECIMAL_DIG == (int)(LDBL_MANT_DIG * 0.3010299956639812 + 2));
+
+  /* Check the value of LDBL_TRUE_MIN.  */
+  ASSERT (LDBL_TRUE_MIN > 0.0L);
+  {
+    volatile long double x = LDBL_TRUE_MIN * 0.5L;
+    ASSERT (x == 0.0L);
+  }
+
+  /* Check the value of LDBL_IS_IEC_60559.  */
+#if (LDBL_MANT_DIG == 53 || LDBL_MANT_DIG == 113) && !defined __m68k__
+  ASSERT (LDBL_IS_IEC_60559);
+#elif 0
+  /* It is not clear what this macro actually means.  Cf.
+     
<http://mailman.oakapple.net/pipermail/cfp-interest/2023-April/002760.html> */
+  ASSERT (!LDBL_IS_IEC_60559);
+#endif
+
+  /* Check the value of LDBL_NORM_MAX.  */
+  ASSERT (LDBL_NORM_MAX == LDBL_MAX);
+
+  /* Check the value of LDBL_SNAN.  */
+  ASSERT (isnanl (LDBL_SNAN));
 }
 
 int




Reply via email to