On Linux/mips (all 3 ABIs) and on Linux/hppa (configuring a testdir in such
a way that the configuration ignores the totalorder* functions provided by
glibc and instead uses the gnulib replacement instead:
  gl_cv_func_totalorder_in_libm=no gl_cv_func_totalorder_no_libm=no \
  gl_cv_func_totalorderf_in_libm=no gl_cv_func_totalorderf_no_libm=no \
  gl_cv_func_totalorderl_in_libm=no gl_cv_func_totalorderl_no_libm=no \
  ./configure
), I see these test failures:

FAIL: test-totalorder
=====================

Failed: i=0 j=1
Failed: i=1 j=0
Failed: i=12 j=13
Failed: i=13 j=12
FAIL test-totalorder (exit status: 1)

FAIL: test-totalorderf
======================

Failed: i=0 j=1
Failed: i=1 j=0
Failed: i=12 j=13
Failed: i=13 j=12
FAIL test-totalorderf (exit status: 1)

FAIL: test-totalorderl
======================

Failed: i=0 j=1
Failed: i=1 j=0
Failed: i=12 j=13
Failed: i=13 j=12
FAIL test-totalorderl (exit status: 1)

That is, QNaN and SNaN produce a wrong comparison result. See the explanation
at the top of lib/snan.h.

This patch fixes the totalorder* replacements, to work right according to the
specification.

Possibly, on the mips side, the code should test __mips_nan2008. But so far
I have not encountered any mips machine where this would be necessary.


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

        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 <float.h>.
        (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'.

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 <config.h>
 
+/* Specification.  */
 #include <math.h>
 
 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 <config.h>
 
+/* Specification.  */
 #include <math.h>
 
 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 <config.h>
 
+/* Specification.  */
 #include <math.h>
 
+#include <float.h>
+
 #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]




Reply via email to