簡體   English   中英

如何比較長雙打與qsort和NaN?

[英]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 <= ba不是a數或b不是a數時, a > b為假。 所以a > b!(a <= b)因為如果其中一個是NaN,它們會產生相反的結果。

如果比較函數使用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 <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時,使用>=, >, <, <=會導致“無效”浮點異常。 因此, @ usr2564301NaN先前測試是謹慎的

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM