如何使用
qsort()
比较长双精度浮点数,并考虑非数字?
当对可能包含非数字的数组进行排序时,我想将所有这些NAN
放在排序后数组的一端。
qsort()
对比较函数有一些限制。
如果第一个参数被认为小于、等于或大于第二个参数,则函数应返回小于、等于或大于零的整数。
C11dr §7.22.5.2 3当将相同的对象多次传递给比较函数时,结果应相互一致。也就是说,对于
qsort
,它们应在数组上定义一个完全排序,...相同的对象始终以相同的方式与关键字进行比较。
§7.22.5 4
a > b
当且仅当 a <= b
或者 a
不是数字或者 b
不是数字时为假。因此,a > b
不等于 !(a <= b)
,因为如果其中一个是 NaN,则它们具有相反的结果。
return (a > b) - (a < b);
,则当一个或两个a
或b
为NaN时,代码将返回0。数组将无法按所需排序,失去了完全排序的要求。在使用类别函数(例如
int isnan(real-floating x)
或int isfinite(real-floating x)
)时,这种排序中long double
方面非常重要。我知道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值。
isnan()
,但我的要求并不苛刻,不需要识别不同类型的 NaN。我知道有信号和非信号 NaN;我相信有许多位模式表示 NaN。但我从未需要详细研究它们以了解在isnan()
表面下发生了什么。一个关键点是,如果数组中有两个元素——比如x[10]
和x[30]
——那么比较x[10]
和x[30]
应该产生与比较x[30]
和x[10]
一致的结果。如果一个是负数,另一个必须是正数或者两个都是零。 - Jonathan Leffler