How to compare long doubles with qsort and with regard to NaN?
How to compare long doubles with
qsort() and with regard to not-a-number?
号
在对可能包含非数字的数组进行排序时,我想将所有这些
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 3When 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
号
当
如果compare函数使用
当使用诸如
我试过下面的。很明显,它是按需要分类的。
子问题:下面的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | #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(" sorted:"); qsort(x, n, sizeof x[0], compare); for (size_t i = 0; i < n; i++) { printf("%.3Le,", x[i]); } puts(""); } |
产量
1 2 | 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一起正常工作还没有足够的信心。
这只是对您的测试进行简单的重新排序,但是如果您愿意的话,它会使
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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; } |
由于EDOCX1[0]的测试在顶部,不应通过任何NAN,因此可以安全地用您的
1 | return (a > b) - (a < b); |
号
除了讨论不同类型的
对于clang,
为了完成,我用EDCOX1,9和EDCOX1,10(来自C++ EDCOX1,11)进行测试,同样,排序顺序没有差异。
NAN测试
1 | int isnan(real-floating x); |
The
isnan macro determines whether its argument value is a NaN. First, an argument represented in a format wider than its semantic type is converted to its semantic type. Then determination is based on the type of the argument.235
235 For theisnan macro, the type for determination does not matter unless the implementation supports NaNs in the evaluation type but not in the semantic type.
号
除了在一个罕见的平台上,
在许多平台上,代码可以使用
比较
如果至少有一个操作数是NaN,则使用
C提供宏
宏分类的挑战在于语义类型可能比
以下使用了上述思想,正如我所读到的,C规范对于除少数情况外的所有情况都是定义良好且功能正确的:当
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #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 } |
。
注意:在阅读了许多好的评论并学习了很多之后,我会按照"我能回答我自己的问题吗?"中的规定发布这个自我回答。除了接受另一个答案。