+2025-07-08 Paul Eggert <eggert@cs.ucla.edu>
+
+ float-h: work around GCC bug 120993
+ Also, fix a too-low LDBL_MAX on this platform, a problem that
+ contributed to LDBL_NORM_MAX test failures with C23 PowerPC GCC
+ with ibmlongdouble.
+ * lib/float.c (gl_LDBL_MAX): On PowerPC with ibmlongdouble, the
+ correct value is 2**1024 - 2**918, not 2**1024 - 2**971. Also,
+ use C99 hex double notation for the divisor, as it’s clearer and
+ should work nowadays.
+ * lib/float.in.h: Simplify by not worring whether it’s AIX or
+ GNU/Linux when redefining LDBL_* macros on PowerPC GCC with
+ ibmlongdouble, as the problem seems to be universal then.
+ (LDBL_NORM_MAX): On PowerPC GCC with ibmlongdouble, define to be
+ LDBL_MAX, so that it’s 2**1024 - 2**918, doubling GCC’s value
+ if supplied.
+ * modules/float-h-tests (Depends-on): Remove floorl, ldexpl.
+ Add truncl.
+ (test_float_h_LDADD): Likewise for libraries.
+ * tests/test-float-h.c (test_isfinitel): New static function,
+ so that we need not rely on isfinitel.
+ (normalize_long_double): Work even if X is negative or not finite.
+ Do not rely on frexpl or ldexpl. Use truncl instead of floorl
+ so that the negative results are consistent with positive.
+ Defend against fritzy PowerPC long double arithmetic.
+
2025-07-08 Collin Funk <collin.funk1@gmail.com>
options: Add reminder to include <config.h>.
@samp{double}.
On FreeBSD/x86 6.4, they represent the incorrect 53-bit precision assumptions
in the compiler, not the real 64-bit precision at runtime.
-On Linux/PowerPC with GCC 4.4, and on AIX 7.1 with GCC 4.2,
+On PowerPC with gcc 15 and the default @option{-mabi=ibmlongdouble},
they don't reflect the ``double double'' representation of @code{long double}
correctly.
@item
#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 } };
+ { { DBL_MAX, DBL_MAX / 0x1p53 } };
# elif defined __i386__
const union gl_long_double_union gl_LDBL_MAX =
{ { 0xFFFFFFFF, 0xFFFFFFFF, 32766 } };
# define LDBL_MAX_10_EXP 4932
#endif
-/* On AIX 7.1 with gcc 4.2, the values of LDBL_MIN_EXP, LDBL_MIN, LDBL_MAX are
- wrong.
- On Linux/PowerPC with gcc 4.4, the value of LDBL_MAX is wrong. */
-#if (defined _ARCH_PPC || defined _POWER) && defined _AIX && (LDBL_MANT_DIG == 106) && defined __GNUC__
+/* On PowerPC with gcc 15 when using __ibm128 long double, the value of
+ LDBL_MIN_EXP, LDBL_MIN, LDBL_MAX, and LDBL_NORM_MAX are wrong. */
+#if ((defined _ARCH_PPC || defined _POWER) && LDBL_MANT_DIG == 106 \
+ && defined __GNUC__)
# undef LDBL_MIN_EXP
# define LDBL_MIN_EXP DBL_MIN_EXP
# undef LDBL_MIN_10_EXP
# define LDBL_MIN_10_EXP DBL_MIN_10_EXP
# undef LDBL_MIN
# define LDBL_MIN 2.22507385850720138309023271733240406422e-308L /* DBL_MIN = 2^-1022 */
-#endif
-#if (defined _ARCH_PPC || defined _POWER) && (defined _AIX || defined __linux__) && (LDBL_MANT_DIG == 106) && defined __GNUC__ && !defined __LDBL_NORM_MAX__
# undef LDBL_MAX
-/* LDBL_MAX is represented as { 0x7FEFFFFF, 0xFFFFFFFF, 0x7C8FFFFF, 0xFFFFFFFF }.
- It is not easy to define:
- #define LDBL_MAX 1.79769313486231580793728971405302307166e308L
- is too small, whereas
- #define LDBL_MAX 1.79769313486231580793728971405302307167e308L
- is too large. Apparently a bug in GCC decimal-to-binary conversion.
- Also, I can't get values larger than
- #define LDBL63 ((long double) (1ULL << 63))
- #define LDBL882 (LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63)
- #define LDBL945 (LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63)
- #define LDBL1008 (LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63)
- #define LDBL_MAX (LDBL1008 * 65535.0L + LDBL945 * (long double) 9223372036821221375ULL + LDBL882 * (long double) 4611686018427387904ULL)
- which is represented as { 0x7FEFFFFF, 0xFFFFFFFF, 0x7C8FFFFF, 0xF8000000 }.
- So, define it like this through a reference to an external variable
+/* LDBL_MAX is 2**1024 - 2**918, represented as: { 0x7FEFFFFF, 0xFFFFFFFF,
+ 0x7C9FFFFF, 0xFFFFFFFF }.
+
+ Do not write it as a constant expression, as GCC would likely treat
+ that as infinity due to the vagaries of this platform's funky arithmetic.
+ Instead, define it through a reference to an external variable.
+ Like the following, but using a union to avoid type mismatches:
- const double LDBL_MAX[2] = { DBL_MAX, DBL_MAX / (double)134217728UL / (double)134217728UL };
+ const double LDBL_MAX[2] = { DBL_MAX, DBL_MAX / 0x1p53 };
extern const long double LDBL_MAX;
- or through a pointer cast
+ The following alternative would not work as well when GCC is optimizing:
+
+ #define LDBL_MAX (*(long double const *) (double[])
+ { DBL_MAX, DBL_MAX / 0x1p53 })
+
+ The following alternative would require GCC 6 or later:
- #define LDBL_MAX \
- (*(const long double *) (double[]) { DBL_MAX, DBL_MAX / (double)134217728UL / (double)134217728UL })
+ #define LDBL_MAX __builtin_pack_longdouble (DBL_MAX, DBL_MAX / 0x1p53)
- Unfortunately, this is not a constant expression, and the latter expression
- does not work well when GCC is optimizing.. */
+ Unfortunately none of the alternatives are constant expressions. */
# if !GNULIB_defined_long_double_union
union gl_long_double_union
{
# endif
extern const union gl_long_double_union gl_LDBL_MAX;
# define LDBL_MAX (gl_LDBL_MAX.ld)
+# undef LDBL_NORM_MAX
+# define LDBL_NORM_MAX LDBL_MAX
#endif
/* On IRIX 6.5, with cc, the value of LDBL_MANT_DIG is wrong.
#ifndef LDBL_NORM_MAX
# ifdef __LDBL_NORM_MAX__
# define LDBL_NORM_MAX __LDBL_NORM_MAX__
-# elif FLT_RADIX == 2 && LDBL_MAX_EXP == 1024 && LDBL_MANT_DIG == 106
-# define LDBL_NORM_MAX 8.98846567431157953864652595394501E+307L
# else
# define LDBL_NORM_MAX LDBL_MAX
# endif
Depends-on:
fpieee
fpucw
-floorl
frexpl
-ldexpl
isnanf-nolibm
isnand-nolibm
isnanl-nolibm
+truncl
float-h-c++-tests
configure.ac:
Makefile.am:
TESTS += test-float-h
check_PROGRAMS += test-float-h
-test_float_h_LDADD = $(LDADD) @FLOORL_LIBM@ @FREXPL_LIBM@ @LDEXPL_LIBM@
+test_float_h_LDADD = $(LDADD) @FREXPL_LIBM@ @TRUNCL_LIBM@
/* -------------------- Check macros for 'long double' -------------------- */
-/* Return X after normalization. This makes a difference on PowerPC
- platforms where long double uses a "double double" format that does
- not conform to the C23 rules for floating point. On such a platform,
- FLT_RADIX = 2, LDBL_MANT_DIG = 106, LDBL_EPSILON = 2**-105, and
- 1 < 1 + e < 1 + LDBL_EPSILON, where e = 2**-106 and 1 + e is a
- representable long double value that is not normalized.
- On such a platform, normalize_long_double (1 + e) returns 1. */
+static int
+test_isfinitel (long double volatile x)
+{
+ if (x != x)
+ return 0;
+ long double volatile zero = x * 0;
+ return zero == 0;
+}
+
+/* Return X after normalization. This makes a difference on platforms
+ where long double can represent unnormalized values. For example,
+ suppose x = 1 + 2**-106 on PowerPC with IBM long double where
+ FLT_RADIX = 2, LDBL_MANT_DIG = 106, and LDBL_EPSILON = 2**-105.
+ Then 1 < x < 1 + LDBL_EPSILON, and normalize_long_double (x) returns 1. */
static long double
normalize_long_double (long double volatile x)
{
- if (FLT_RADIX == 2)
+ if (FLT_RADIX == 2 && test_isfinitel (x))
{
int xexp;
- long double volatile xfrac = frexpl (x, &xexp);
- x = ldexpl (floorl (ldexpl (xfrac, LDBL_MANT_DIG)),
- xexp - LDBL_MANT_DIG);
+ long double volatile
+ frac = frexpl (x, &xexp),
+ significand = frac * pow2l (LDBL_MANT_DIG),
+ normalized_significand = truncl (significand),
+ normalized_x = normalized_significand * pow2l (xexp - LDBL_MANT_DIG);
+
+ /* The test_isfinitel defends against PowerPC with IBM long double,
+ which fritzes out near LDBL_MAX. */
+ if (test_isfinitel (normalized_x))
+ x = normalized_x;
}
else
{