c - 如何将 long double 与 qsort 以及 NaN 进行比较?

标签 c qsort

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

在对可能包含非数字的数组进行排序时,我想将所有这些 NAN到排序数组的一端。


qsort()对比较功能施加了一些限制。

The function shall return an integer less than, equal to, or greater than zero if the first argument is considered to be respectively less than, 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 shall be consistent with one another. That is, for qsort they shall define a total ordering on the array, ... the same object shall always compare the same way with the key.
§7.22.5 4

a > b a <= b 时为假或者如果 a不是数字或者如果 b不是数字。所以a > b!(a <= b) 不同因为如果其中之一为 NaN,则结果相反。

如果比较函数使用return (a > b) - (a < b); , 如果一个或两个 a 代码将返回 0或 b是 NaN。该数组不会按需要排序,并且它失去了总排序要求。

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,

如果我知道比较功能是正确的,我会在 Code Review 上发布改进想法。然而,我对代码能正确处理那些讨厌的 NaN 没有足够的信心。

最佳答案

这只是对测试进行简单的重新排序,但它使状态变为 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 的不同类型 (听起来有点像有多少天使可以在 CPU 内核上跳舞),这对于您的目的来说应该足够稳定,我看不出这段代码有任何可能的问题。

有了 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(); (来自 C++ <limits.h> )——同样,排序顺序没有区别。

关于c - 如何将 long double 与 qsort 以及 NaN 进行比较?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48069404/

相关文章:

c - qsort 在 c 中按字典顺序对字符串进行排序

c++ - SDL2 Alpha 未出现

c - 使用 qsort 的段错误

c - C中的快速排序实现

c - 使用 qsort() 对结构数组中的 double 进行排序

使用 qsort() 使用函数指针按照用户想要的顺序进行排序的 C 代码

c++ - 访问违规写入位置,在我的循环中

我们可以将 6 字节整数编码为 4 字节整数吗?

c - 直接声明并赋值给 C 指针

c - 供应代码正则表达式