How to compare long doubles with qsort and in terms of NaN?

How to compare long doubles with qsort()and with respect to not-a-number ?

When sorting an array that can contain non-digits, I would like to put all those NANat one end of the sorted array.


qsort() imposes some restriction on the comparison function.

The function must return an integer less than, equal to or greater than zero, if the first argument is considered less, equal to or greater than the second.
C11dr ยง7.22.5.2 3

When the same objects ... are passed more than once to the comparison function, the results must be consistent with each other. That is, for qsortthey must determine the full ordering in the array, ... the same object will always compare the same path with the key.
ยง7.22.5 4

a > bfalse if a <= bor if ait is not a number or if bit is not a number. So, it a > bdoes not coincide with !(a <= b), because they have opposite results if one of them is NaN.

If the comparison function uses the return (a > b) - (a < b);code returns 0 if one or both a, or bis NaN. The array does not sort at will and loses the general order requirement.

long double , int isnan(real-floating x); int isfinite(real-floating x);. , isfinite( finite_long_double_more_than_DBL_MAX) false. , isnan(some_long_double) - .


. , -, .

-: compare() ? ? - ? ( 0.0L -0.0L )

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <float.h>

int compare(const void *a, const void *b) {
  const long double *fa = (const long double *) a;
  const long double *fb = (const long double *) b;
  if (*fa > *fb) return 1;
  if (*fa < *fb) return -1;

  if (*fa == *fb) {
    //return -memcmp(fa, fb, sizeof *fa); if -0.0, 0.0 order important.
    return 0;
  }
  // At least one of *fa or *fb is NaN
  // is *fa a non-NaN?
  if (!isnan(*fa)) return -1;
  if (!isnan(*fb)) return 1;

  // both NaN
  return 0;
  // return -memcmp(fa, fb, tbd size); if NaN order important.
}

int main(void) {
  long double x[] = { 0.0L / 0.0, 0.0L / 0.0, 0.0, 1.0L / 0.0, -0.0, LDBL_MIN,
      LDBL_MAX, 42.0, -1.0L / 0.0, 867-5309, -0.0 };
  x[0] = -x[0];
  printf("unsorted: ");
  size_t n = sizeof x / sizeof x[0];
  for (size_t i = 0; i < n; i++) {
    printf("%.3Le,", x[i]);
  }
  printf("\nsorted: ");
  qsort(x, n, sizeof x[0], compare);
  for (size_t i = 0; i < n; i++) {
    printf("%.3Le,", x[i]);
  }
  puts("");
}

unsorted: nan,-nan,0.000e+00,inf,-0.000e+00,3.362e-4932,1.190e+4932,4.200e+01,-inf,-4.442e+03,-0.000e+00,
sorted: -inf,-4.442e+03,-0.000e+00,0.000e+00,-0.000e+00,3.362e-4932,4.200e+01,1.190e+4932,inf,nan,-nan,

, , . , NaN.

+6
2

, NaN , .

int compare(const void *a, const void *b)
{
    const long double fa = *(const long double *) a;
    const long double fb = *(const long double *) b;

    if (isnan(fa))
    {
        if (isnan(fb))
        {
            return 0;
        }
        return 1;
    }
    if (isnan(fb))
    {
        return -1;
    }
    if (fa > fb) return 1;
    if (fa < fb) return -1;

    /* no more comparisons needed */
    return 0;
}

NaN NaN ,

return (a > b) - (a < b);

NaN ( , ), , .

Clang -ffast-math, -fdenormal-fp-math=[ieee|preserve-sign|positive-zero] . gcc -ffast-math, -funsafe-math-optimizations -ffinite-math-only (, , , , , NaN).

, std::numeric_limits<double>::signaling_NaN();, std::numeric_limits<double>::quiet_NaN(); ( ++ <limits.h>) - , .

+5

NaN test

int isnan(real-floating x);

isnan , NaN. -, , , , , . . 235
235 isnan , NaN , .

isnan(some_long_double) , .

int isunordered(real-floating x, real-floating y) isnan() , .

(a == a) NaN-, 0, a NaN 1 . , __STDC_IEC_559__, .



>=, >, <, <= C11 7.12.14

>=, >, <, <=, NaN, "" . , NaN , @usr2564301

C isgreaterequal(), isgreaterequal(), isless(), islessthna(), "" . double, , long double. isgreater(long_double_a, long_double_a) double .

, long double.


, C spec, , : long double NaN, real-floating ( double),.

#include <math.h>

// compare 2 long double.  All NaN are greater than numbers.
int compare(const void *a, const void *b) {
  const long double *fa = (const long double *) a;
  const long double *fb = (const long double *) b;

  if (!isunordered(*fa, *fb)) {
    return (*fa > *fb) - (*fa < *fb);
  }

  if (!isnan(*fa)) {
    return -1;
  }
  return isnan(*fb);  // return 0 or 1
}

: , , ? .

+2

All Articles