From 979149c7a5b82bdc041f530de8661ebc3e0bdb41 Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Sat, 14 Oct 2023 20:48:13 +0200 Subject: [PATCH] totalorder*: Fix test failures on PA-RISC and MIPS CPUs. * lib/totalorderf.c (totalorderf): On hppa and mips, invert bit 22 before comparing two NaNs. * lib/totalorder.c (totalorder): On hppa and mips, invert bit 51 before comparing two NaNs. * lib/totalorderl.c: Include . (totalorderl): On hppa and mips, invert bit 51 or 47 of the xhi, yhi parts before comparing two NaNs. * modules/totalorderl (Depends-on): Add 'float'. --- ChangeLog | 12 ++++++++++++ lib/totalorder.c | 18 +++++++++++++++--- lib/totalorderf.c | 22 +++++++++++++++++----- lib/totalorderl.c | 23 +++++++++++++++++------ modules/totalorderl | 1 + 5 files changed, 62 insertions(+), 14 deletions(-) diff --git a/ChangeLog b/ChangeLog index f4db2f61c1..60f9cbd63a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2023-10-14 Bruno Haible + + totalorder*: Fix test failures on PA-RISC and MIPS CPUs. + * lib/totalorderf.c (totalorderf): On hppa and mips, invert bit 22 + before comparing two NaNs. + * lib/totalorder.c (totalorder): On hppa and mips, invert bit 51 before + comparing two NaNs. + * lib/totalorderl.c: Include . + (totalorderl): On hppa and mips, invert bit 51 or 47 of the xhi, yhi + parts before comparing two NaNs. + * modules/totalorderl (Depends-on): Add 'float'. + 2023-10-14 Bruno Haible totalorder* tests: Test also the signalling NaNs. diff --git a/lib/totalorder.c b/lib/totalorder.c index 4c861f971c..edbcd6998b 100644 --- a/lib/totalorder.c +++ b/lib/totalorder.c @@ -18,26 +18,38 @@ #include +/* Specification. */ #include int totalorder (double const *x, double const *y) { - /* Although the following is not strictly portable, and won't work - on some obsolete platforms (e.g., PA-RISC, MIPS before alignment - to IEEE 754-2008), it should be good enough nowadays. */ + /* If the sign bits of *X and *Y differ, the one with non-zero sign bit + is "smaller" than the one with sign bit == 0. */ int xs = signbit (*x); int ys = signbit (*y); if (!xs != !ys) return xs; + + /* If one of *X, *Y is a NaN and the other isn't, the answer is easy + as well: the negative NaN is "smaller", the positive NaN is "greater" + than the other argument. */ int xn = isnand (*x); int yn = isnand (*y); if (!xn != !yn) return !xn == !xs; + /* If none of *X, *Y is a NaN, the '<=' operator does the job, including + for -Infinity and +Infinity. */ if (!xn) return *x <= *y; + /* At this point, *X and *Y are NaNs with the same sign bit. */ + unsigned long long extended_sign = -!!xs; +#if defined __hppa || defined __mips__ + /* Invert the most significant bit of the mantissa field. Cf. snan.h. */ + extended_sign ^= (1ULL << 51); +#endif union { unsigned long long i; double f; } volatile xu = {0}, yu = {0}; xu.f = *x; yu.f = *y; diff --git a/lib/totalorderf.c b/lib/totalorderf.c index d86299ff09..4cbfa06ffd 100644 --- a/lib/totalorderf.c +++ b/lib/totalorderf.c @@ -18,27 +18,39 @@ #include +/* Specification. */ #include int totalorderf (float const *x, float const *y) { - /* Although the following is not strictly portable, and won't work - on some obsolete platforms (e.g., PA-RISC, MIPS before alignment - to IEEE 754-2008), it should be good enough nowadays. */ + /* If the sign bits of *X and *Y differ, the one with non-zero sign bit + is "smaller" than the one with sign bit == 0. */ int xs = signbit (*x); int ys = signbit (*y); if (!xs != !ys) return xs; + + /* If one of *X, *Y is a NaN and the other isn't, the answer is easy + as well: the negative NaN is "smaller", the positive NaN is "greater" + than the other argument. */ int xn = isnanf (*x); int yn = isnanf (*y); if (!xn != !yn) return !xn == !xs; + /* If none of *X, *Y is a NaN, the '<=' operator does the job, including + for -Infinity and +Infinity. */ if (!xn) return *x <= *y; - unsigned long extended_sign = -!!xs; - union { unsigned long i; float f; } volatile xu = {0}, yu = {0}; + /* At this point, *X and *Y are NaNs with the same sign bit. */ + + unsigned int extended_sign = -!!xs; +#if defined __hppa || defined __mips__ + /* Invert the most significant bit of the mantissa field. Cf. snan.h. */ + extended_sign ^= (1U << 22); +#endif + union { unsigned int i; float f; } volatile xu = {0}, yu = {0}; xu.f = *x; yu.f = *y; return (xu.i ^ extended_sign) <= (yu.i ^ extended_sign); diff --git a/lib/totalorderl.c b/lib/totalorderl.c index cc0698febe..6982762a0f 100644 --- a/lib/totalorderl.c +++ b/lib/totalorderl.c @@ -18,8 +18,11 @@ #include +/* Specification. */ #include +#include + #ifndef LDBL_SIGNBIT_WORD # define LDBL_SIGNBIT_WORD (-1) #endif @@ -27,10 +30,6 @@ int totalorderl (long double const *x, long double const *y) { - /* Although the following is not strictly portable, and won't work - on some obsolete platforms (e.g., PA-RISC, MIPS before alignment - to IEEE 754-2008), it should be good enough nowadays. */ - /* If the sign bits of *X and *Y differ, the one with non-zero sign bit is "smaller" than the one with sign bit == 0. */ int xs = signbit (*x); @@ -56,6 +55,10 @@ totalorderl (long double const *x, long double const *y) if (sizeof (long double) <= sizeof (unsigned long long)) { +#if defined __hppa || defined __mips__ + /* Invert the most significant bit of the mantissa field. Cf. snan.h. */ + extended_sign ^= (1ULL << 51); +#endif union { unsigned long long i; long double f; } volatile xu = {0}, yu = {0}; xu.f = *x; @@ -63,6 +66,14 @@ totalorderl (long double const *x, long double const *y) return (xu.i ^ extended_sign) <= (yu.i ^ extended_sign); } + unsigned long long extended_sign_hi = extended_sign; +#if defined __hppa || defined __mips__ + /* Invert the most significant bit of the mantissa field. Cf. snan.h. */ + extended_sign_hi ^= + (1ULL << (LDBL_MANT_DIG == 106 + ? 51 /* double-double representation */ + : (LDBL_MANT_DIG - 2) - 64)); /* quad precision representation */ +#endif union u { unsigned long long i[2]; long double f; } volatile xu, yu; /* Although it is tempting to initialize with {0}, Solaris cc (Sun C 5.8) on x86_64 miscompiles {0}: it initializes only the lower 80 bits, @@ -87,8 +98,8 @@ totalorderl (long double const *x, long double const *y) bigendian = LDBL_SIGNBIT_WORD < sizeof xu.i[0] / sizeof (unsigned); unsigned long long - xhi = xu.i[!bigendian] ^ extended_sign, - yhi = yu.i[!bigendian] ^ extended_sign, + xhi = xu.i[!bigendian] ^ extended_sign_hi, + yhi = yu.i[!bigendian] ^ extended_sign_hi, xlo = xu.i[ bigendian] ^ extended_sign, ylo = yu.i[ bigendian] ^ extended_sign; return (xhi < yhi) | ((xhi == yhi) & (xlo <= ylo)); diff --git a/modules/totalorderl b/modules/totalorderl index ed6f801130..76dafb8ecd 100644 --- a/modules/totalorderl +++ b/modules/totalorderl @@ -10,6 +10,7 @@ m4/signbit.m4 Depends-on: math extensions +float [test $HAVE_TOTALORDERL = 0 || test $REPLACE_TOTALORDERL = 1] stdbool [test $HAVE_TOTALORDERL = 0 || test $REPLACE_TOTALORDERL = 1] isnanl [test $HAVE_TOTALORDERL = 0 || test $REPLACE_TOTALORDERL = 1] signbit [test $HAVE_TOTALORDERL = 0 || test $REPLACE_TOTALORDERL = 1] -- 2.39.5