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

如何比较长双打与qsort()和关于非数字 ?

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


qsort()对比较函数施加了一些限制。

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

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

a <= ba不是a数或b不是a数时, a > b为假。 所以a > b!(a <= b)因为如果其中一个是NaN,它们会产生相反的结果。

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

当使用像int isnan(real-floating x);这样的分类函数时,这种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  #include  #include  #include  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, 

如果我知道比较function是正确的,我会发布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::signaling_NaN();std::numeric_limits::quiet_NaN(); (来自C ++ ) - 同样,排序顺序没有区别。

NaN测试

 int isnan(real-floating x); 

isnan宏确定其参数值是否为NaN。 首先,以比其语义类型更宽的格式表示的参数被转换为其语义类型。 然后确定基于参数的类型。 235
235对于isnan宏,除非实现在评估类型中支持NaN,但在语义类型中不支持NaN,否则确定类型无关紧要。

除了在一个罕见的平台上, isnan(some_long_double)将按预期工作。

int isunordered(real-floating x, real-floating y)就像isnan()一样,它会占两个参数。

许多平台上,代码可以使用(a == a)作为候选NaN测试,当a是NaN时评估为0 ,否则为1 。 不幸的是,除非实现定义了__STDC_IEC_559__ ,否则这肯定不起作用。


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

当至少一个操作数是NaN时,使用>=, >, <, <=会导致“无效”浮点exception。 因此, @ usr2564301对NaN先前测试是谨慎的

C提供宏isgreaterequal(), isgreaterequal(), isless(), islessthna() ,它们进行比较而不引发“无效”浮点exception。 这是double好选择,但宏使用的是real-floating ,它可能与long double不同。 isgreater(long_double_a, long_double_a)可以计算为double并且不提供所需的比较结果。

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


以下使用了上述想法,并且正如我所读到的,C规范在所有情况下都有明确的定义和function正确,除了罕见的情况:当long double有NaN而不是real-floating (通常是double )时没有。

 #include  // 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 } 

注意:在阅读了许多好的评论并学到很多东西之后,我发布了这个自我答案, 我可以回答我自己的问题吗? 除了接受另一个答案。