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 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
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/