[英]How to compare long doubles with qsort and with regard to NaN?
如何比較長雙打與
qsort()
和關於非數字 ?
在排序可能包含非數字的數組時,我想將所有這些NAN
放在已排序數組的一端。
qsort()
對比較函數施加了一些限制。
如果第一個參數被認為分別小於,等於或大於第二個參數,則該函數應返回小於,等於或大於零的整數。
C11dr§7.22.5.23當相同的對象...不止一次傳遞給比較函數時,結果應該彼此一致。 也就是說,對於
qsort
它們應該在數組上定義一個總排序 ,......同一個對象應始終以相同的方式與密鑰進行比較。
§7.22.54
當a <= b
或a
不是a數或b
不是a數時, a > b
為假。 所以a > b
與!(a <= b)
因為如果其中一個是NaN,它們會產生相反的結果。
如果比較函數使用return (a > b) - (a < b);
如果a
或b
中的一個或兩個是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 <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>
) - 同樣,排序順序沒有區別。
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測試,當a
是NaN時評估為0
,否則為1
。 不幸的是,除非實現定義了__STDC_IEC_559__
,否則這肯定不起作用。
比較
>=, >, <, <=
和C11 7.12.14比較宏
當至少一個操作數是NaN時,使用>=, >, <, <=
會導致“無效”浮點異常。 因此, @ usr2564301對NaN
先前測試是謹慎的
C提供宏isgreaterequal(), isgreaterequal(), isless(), islessthna()
,它們進行比較而不引發“無效”浮點異常。 這是double
好選擇,但宏使用的是real-floating ,可能與long double
不同。 isgreater(long_double_a, long_double_a)
可以計算為double
並且不提供所需的比較結果。
分類宏的挑戰是語義類型可能比long double
窄。
下面使用上面的想法,並且正如我所讀到的那樣,C規范在所有情況下都很好地定義和功能正確,除了罕見的情況:當long double
有NaN而不是real-floating (通常是double
)時沒有。
#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
}
注意:在閱讀了許多好的評論並且學到了很多東西后,我發布了這個自我回答, 我可以回答我自己的問題嗎? 除了接受另一個答案。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.