This is in response to the GCC developers; see <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=120993>. * lib/float.c (gl_LDBL_MAX): Remove the implementation for PowerPC, as it should no longer be needed. * lib/float.in.h (LDBL_MIN_EXP, LDBL_MIN_10_EXP, LDBL_MIN) (LDBL_MAX, LDBL_EPSILON, LDBL_NORM_MAX): On PowerPC with IBM long double, simplify by using the GCC 15 values unconditionally. These are the correct values according to the GCC developers and there seems little point to disagreeing with current GCC about obsolescent arithmetic that is so problematic in practice. * tests/test-float-h.c (test_long_double): Relax tests of LDBL_MAX when !LDBL_IS_IEC_60559, as the tests would now fail on PowerPC and they were not portable in that case anyway. --- ChangeLog | 17 ++++++++ doc/posix-headers/float.texi | 7 ++- lib/float.c | 5 +-- lib/float.in.h | 85 ++++++++++-------------------------- tests/test-float-h.c | 32 +++++++------- 5 files changed, 64 insertions(+), 82 deletions(-)
diff --git a/ChangeLog b/ChangeLog index 33bd581f88..a5a766deec 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +2025-07-28 Paul Eggert <egg...@cs.ucla.edu> + + float-h: change IBM long double to match GCC 15 + This is in response to the GCC developers; + see <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=120993>. + * lib/float.c (gl_LDBL_MAX): Remove the implementation for PowerPC, + as it should no longer be needed. + * lib/float.in.h (LDBL_MIN_EXP, LDBL_MIN_10_EXP, LDBL_MIN) + (LDBL_MAX, LDBL_EPSILON, LDBL_NORM_MAX): On PowerPC with IBM long + double, simplify by using the GCC 15 values unconditionally. + These are the correct values according to the GCC developers + and there seems little point to disagreeing with current GCC + about obsolescent arithmetic that is so problematic in practice. + * tests/test-float-h.c (test_long_double): Relax tests of LDBL_MAX + when !LDBL_IS_IEC_60559, as the tests would now fail on PowerPC and + they were not portable in that case anyway. + 2025-07-23 Collin Funk <collin.fu...@gmail.com> posixtm tests: Avoid test failure on Haiku. diff --git a/doc/posix-headers/float.texi b/doc/posix-headers/float.texi index 1fbdd29114..ce2349ea0e 100644 --- a/doc/posix-headers/float.texi +++ b/doc/posix-headers/float.texi @@ -19,9 +19,12 @@ On OpenBSD 4.0 and MirBSD 10, they are the same as the values of the @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 PowerPC with gcc 15 and the default @option{-mabi=ibmlongdouble}, +On PowerPC with gcc 8.3 and the default @option{-mabi=ibmlongdouble}, they don't reflect the ``double double'' representation of @code{long double} -correctly. +correctly; the non-IEEE representation is tricky, as it is a pair of +IEEE @code{double} values such that adding the two elements and +rounding to even yields the high element of the pair, and arithmetic +overflow has undefined behavior instead of reliably yielding infinity. @item The macros @code{FLT_HAS_SUBNORM}, @code{FLT_DECIMAL_DIG}, @code{FLT_TRUE_MIN}, diff --git a/lib/float.c b/lib/float.c index 780a0d1ca6..593e4bcd1a 100644 --- a/lib/float.c +++ b/lib/float.c @@ -21,10 +21,7 @@ #include <float.h> #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 / 0x1p53 } }; -# elif defined __i386__ +# ifdef __i386__ const union gl_long_double_union gl_LDBL_MAX = { { 0xFFFFFFFF, 0xFFFFFFFF, 32766 } }; # endif diff --git a/lib/float.in.h b/lib/float.in.h index 8d93f5eeaf..ab7ea6d561 100644 --- a/lib/float.in.h +++ b/lib/float.in.h @@ -113,69 +113,31 @@ extern const union gl_long_double_union gl_LDBL_MAX; # define LDBL_MAX_10_EXP 4932 #endif -/* 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__) +/* 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 8.3, the values of LDBL_MAX and LDBL_EPSILON are + wrong. + Assume these bugs are fixed in any GCC new enough + to define __LDBL_NORM_MAX__. */ +#if (defined __powerpc__ && LDBL_MANT_DIG == 106 \ + && defined __GNUC__ && !defined __LDBL_NORM_MAX__) # undef LDBL_MIN_EXP -# define LDBL_MIN_EXP DBL_MIN_EXP +# define LDBL_MIN_EXP (-968) # undef LDBL_MIN_10_EXP -# define LDBL_MIN_10_EXP DBL_MIN_10_EXP +# define LDBL_MIN_10_EXP (-291) # undef LDBL_MIN -# define LDBL_MIN 2.22507385850720138309023271733240406422e-308L /* DBL_MIN = 2^-1022 */ +# define LDBL_MIN 0x1p-969L + +/* IBM long double is tricky: it is represented as the sum of two doubles, + and the high double must equal the sum of the two parts rounded to nearest. + The maximum finite value for which this is true is + { 0x1.fffffffffffffp+1023, 0x1.ffffffffffffep+969 }, + which represents 0x1.fffffffffffff7ffffffffffff8p+1023L. + Although computations can yield representations of numbers larger than this, + these computations are considered to have overflowed and behavior is undefined. + See <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=120993>. */ # undef LDBL_MAX -/* 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 / 0x1p53 }; - extern const long double LDBL_MAX; - - 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 __builtin_pack_longdouble (DBL_MAX, DBL_MAX / 0x1p53) - - Unfortunately none of the alternatives are constant expressions. */ -# if !GNULIB_defined_long_double_union -union gl_long_double_union - { - struct { double hi; double lo; } dd; - long double ld; - }; -# define GNULIB_defined_long_double_union 1 -# 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. - On IRIX 6.5, with gcc 4.2, the values of LDBL_MIN_EXP, LDBL_MIN, LDBL_EPSILON - are wrong. */ -#if defined __sgi && (LDBL_MANT_DIG >= 106) -# undef LDBL_MANT_DIG -# define LDBL_MANT_DIG 106 -# if 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 */ -# undef LDBL_EPSILON -# define LDBL_EPSILON 2.46519032881566189191165176650870696773e-32L /* 2^-105 */ -# endif -#endif +# define LDBL_MAX 0x1.fffffffffffff7ffffffffffff8p+1023L /* On PowerPC platforms, 'long double' has a double-double representation. Up to ISO C 17, this was outside the scope of ISO C because it can represent @@ -187,9 +149,8 @@ extern const union gl_long_double_union gl_LDBL_MAX; numbers with mantissas of the form 1.<52 bits><many zeroes><52 bits> are called "unnormalized". And since LDBL_EPSILON must be normalized (per ISO C 23 ยง 5.2.5.3.3.(33)), it must be 2^-105. */ -#if defined __powerpc__ && LDBL_MANT_DIG == 106 # undef LDBL_EPSILON -# define LDBL_EPSILON 2.46519032881566189191165176650870696773e-32L /* 2^-105 */ +# define LDBL_EPSILON 0x1p-105L #endif /* ============================ ISO C11 support ============================ */ @@ -322,6 +283,8 @@ extern gl_DBL_SNAN_t gl_DBL_SNAN; #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 0x1.ffffffffffffffffffffffffff8p+1022L # else # define LDBL_NORM_MAX LDBL_MAX # endif diff --git a/tests/test-float-h.c b/tests/test-float-h.c index 165278b295..3fcab3ce25 100644 --- a/tests/test-float-h.c +++ b/tests/test-float-h.c @@ -460,21 +460,22 @@ test_long_double (void) ASSERT (LDBL_MAX_10_EXP == (int) (LDBL_MAX_EXP * 0.30103)); /* Check the value of LDBL_MAX. */ - { - volatile long double m = LDBL_MAX; - int n; + if (LDBL_IS_IEC_60559) + { + volatile long double m = LDBL_MAX; + int n; - ASSERT (m + m > m); - for (n = 0; n <= 2 * LDBL_MANT_DIG; n++) - { - volatile long double pow2_n = pow2l (n); /* 2^n */ - volatile long double x = m + (m / pow2_n); - if (x > m) - ASSERT (x + x == x); - else - ASSERT (!(x + x == x)); - } - } + ASSERT (m + m > m); + for (n = 0; n <= 2 * LDBL_MANT_DIG; n++) + { + volatile long double pow2_n = pow2l (n); /* 2^n */ + volatile long double x = m + (m / pow2_n); + if (x > m) + ASSERT (x + x == x); + else + ASSERT (!(x + x == x)); + } + } /* Check the value of LDBL_MIN. */ { @@ -524,7 +525,8 @@ test_long_double (void) #endif /* Check the value of LDBL_NORM_MAX. */ - ASSERT (LDBL_NORM_MAX == normalize_long_double (LDBL_MAX)); + if (LDBL_IS_IEC_60559) + ASSERT (LDBL_NORM_MAX == normalize_long_double (LDBL_MAX)); /* Check the value of LDBL_SNAN. */ ASSERT (isnanl (LDBL_SNAN)); -- 2.48.1