如何比较长双打与qsort和NaN?

时间:2018-01-02 23:48:55

标签: c qsort

  

如何将长双打与qsort()以及not-a-number进行比较?

在排序可能包含非数字的数组时,我想将所有NAN放到已排序数组的一端。

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

  

该函数应返回小于,等于或的整数   如果第一个参数被认为分别小于,等于或大于第二个参数,则大于零   C11dr§7.22.5.23

     

当相同的对象...不止一次传递给比较函数时,结果应该彼此一致。也就是说,对于qsort,他们应该在数组上定义总排序,...同一个对象应始终以相同的方式与键进行比较。
  §7.22.54

a > ba <= b不是数字或a不是数字时,

b为false。因此,a > b!(a <= b)不同,因为如果其中一个是NaN,则会产生相反的结果。

如果比较函数使用return (a > b) - (a < b);,如果ab中的一个或两个都是NaN,则代码将返回0。数组不会按照需要排序,它会丢失总排序要求。

使用long doubleint 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一起工作。

2 个答案:

答案 0 :(得分:5)

这只是对您的测试的简单重新排序,但如果您愿意,它会使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>)进行了测试 - 再次,排序顺序没有区别。

答案 1 :(得分:2)

NaN测试

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为NaN且a时评估为1除此以外。不幸的是,除非实现定义__STDC_IEC_559__,否则不一定有效。

比较
>=, >, <, <= 和C11 7.12.14比较宏

当至少一个操作数是NaN时使用>=, >, <, <=可能导致“无效”浮点异常。因此NaN

回答isgreaterequal(), isgreaterequal(), isless(), islessthna()的先前测试是谨慎的

C提供宏double,它们进行比较而不会引发“无效”浮点 例外。这是long double的不错选择,但宏使用实时浮动,可能与isgreater(long_double_a, long_double_a)不同。 double可能会评估为long double,并且无法提供所需的比较结果。

分类宏的挑战是语义类型可能比long double更窄。

以下使用了上述想法,并且正如我所读到的那样,除了罕见的情况之外,所有情况都明确定义了C规范并且功能正确:当double具有NaN而不是实时浮动(通常#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 } )没有。

CustomerRepository

注意:在阅读了许多好评并且学到很多东西之后,除了接受另一个答案之外,我发布了@usr2564301中提供的自我答案。