1

プログラムでlibresampleを使用しています。しばらくすると(約50分)、1つのワークステーションでlibの関数lrsFilterUD()でクラッシュします。

float lrsFilterUD(float Imp[],  /* impulse response */
              float ImpD[], /* impulse response deltas */
              UWORD Nwing,  /* len of one wing of filter */
              BOOL Interp,  /* Interpolate coefs using deltas? */
              float *Xp,    /* Current sample */
              double Ph,    /* Phase */
              int Inc,    /* increment (1 for right wing or -1 for left) */
              double dhb)
{
   float a;
   float *Hp, *Hdp, *End;
   float v, t;
   double Ho;

   v = 0.0; /* The output value */
   Ho = Ph*dhb;
   End = &Imp[Nwing];
   if (Inc == 1)        /* If doing right wing...              */
   {                      /* ...drop extra coeff, so when Ph is  */
      End--;            /*    0.5, we don't do too many mult's */
      if (Ph == 0)      /* If the phase is zero...           */
         Ho += dhb;     /* ...then we've already skipped the */
   }                         /*    first sample, so we must also  */
                        /*    skip ahead in Imp[] and ImpD[] */

   if (Interp)
      while ((Hp = &Imp[(int)Ho]) < End) {
         t = *Hp;       /* Get IR sample */
         Hdp = &ImpD[(int)Ho];  /* get interp bits from diff table*/
         a = Ho - floor(Ho);      /* a is logically between 0 and 1 */
         t += (*Hdp)*a; /* t is now interp'd filter coeff */
         t *= *Xp;      /* Mult coeff by input sample */
         v += t;            /* The filter output */
         Ho += dhb;     /* IR step */
         Xp += Inc;     /* Input signal step. NO CHECK ON BOUNDS */
      }
   else 
      while ((Hp = &Imp[(int)Ho]) < End) {
         dprintf("while begin: Hp = %p, *Hp = %a, (int)Ho = %d, Imp[(int)Ho] = %a, &Imp[(int)Ho] = %p", Hp, *Hp, (int)Ho, Imp[(int)Ho], &Imp[(int)Ho]);
         t = *Hp;       /* Get IR sample */
         dprintf("before t = %a, *Xp = %a, Xp = %p", t, *Xp, Xp);
         t *= *Xp;      /* Mult coeff by input sample */
         dprintf("after2 t = %a, v = %a", t, v);
         v += t;            /* The filter output */
         dprintf("v = %a", v);
         Ho += dhb;     /* IR step */
         Xp += Inc;     /* Input signal step. NO CHECK ON BOUNDS */
      }

   return v;
}

乗算の前後のt、* Xp、Xpの値をログに記録しました。

while begin: Hp = 0xaf5daa8, *Hp = -0.009034, (int)Ho = 16384, Imp[(int)Ho] = -0.009034, &Imp[(int)Ho] = 0xaf5daa8
before multiplication t = -0.009034, *Xp = 0.000000, Xp = 0xaebe9b8
after multiplication t = nan

このコードは何度も実行され、クラッシュする前に同じt値とXp値があります。

before multiplication t = -0.009034, *Xp = 0.000000, Xp = 0xaebe9c8
after multiplication t = -0.000000, v = 282.423676

または追加の別のケース:

before addition t = -460.799988, v = 0.000000
after addition v = nan

何がナンを引き起こしている可能性がありますか?これは、Linuxではgcc4.1.2でコンパイルされています。

更新:変数を%aとして出力します。結果:

//t = 0x1.2806bap+2
//Hp = 0xb3bb870
t = *Hp;
//t = nan

アップデート2:コードがicpcによってコンパイルされている場合、そのような問題はありません。では、コンパイラ固有の問題はありますか?

4

4 に答える 4

6

明らかに、-0.009034•0.000000はNaNを生成するべきではありません。したがって、問題で提示されたコードとデータが実際の計算を正確に表していないか、計算の実装に欠陥があります。

ハードウェアと基本的なコンピューティングの実装に欠陥がないと仮定した場合、調査する可能性は次のとおりです。

  • のと、乗算の直前の正しい値、または乗算の直後の正しい値のログt*Xp失敗しました。t*Xpt
  • tまたはの値の表示*Xpが正しくありません。たとえば、表示に使用されるフォーマットは、NaNなどの他の値があるに*Xpもかかわらず、「0.000000」を示していました。*Xp
  • Xpどこか不適切なポイントであるため、*Xp信頼性が低くなります(たとえば、外部操作によって変更されます)。
  • 表示されるコードは不正確です。たとえば、変更された古いソースからのものであるか、新しいソースであるが以前にコンパイルされたコードが実行されています。

注:浮動小数点オブジェクトを使用してデバッグする場合は、「%f」などの形式で印刷しないでください。特に、桁数のデフォルト値では印刷しないでください。16進表現を使用して、浮動小数点値の正確な値を出力する「%a」を使用して印刷する必要があります。C実装が浮動小数点値を小数に適切に変換できる場合は、多くの状況で「%.99g」を使用することもできます。

于 2012-12-24T13:20:06.187 に答える
4

Eric Postpischilの他の点では優れた回答が言及されていない5 番目の可能性があります。

  • 乗算は x87 レジスタで実行されており、プログラムの実行における以前の操作 (おそらく無関係) が原因で、浮動小数点スタック オーバーフローが発生しました。プロセッサがこの障害状態にある場合、x87 レジスタで実行されるすべての計算は NaN の結果を生成します。

これの 2 つの最も一般的な原因は、スコープ内にプロトタイプを持たない浮動小数点の結果を返す関数の呼び出し (多くの呼び出し規則では、これにより、呼び出し元が FP スタックから結果をポップすることに失敗することになります)、および不適切なハンドリングです。 -書かれた(おそらくインライン)アセンブリ。

ある程度の時間が経過した後にのみ障害が発生するという事実は、この可能性の証拠を提供します。浮動小数点スタックの要素をリークするめったに使用されないコードパスが 1 つある場合、失敗が明らかになる前に何度か使用する必要があり、これまでは通知を逃れることができた可能性があります。

この可能性を診断または除外するには、浮動小数点ステータス レジスタ (FPSR) のビット 6 (SF) を確認する必要があります。使用しているコンパイラによって、FPSR を検査する正確な方法が異なる場合があります。

于 2012-12-24T14:35:51.810 に答える
1

Wikiから、次のように NaN を返すことができる 3 種類の操作があります。

1. Operations with a NaN as at least one operand.
2. Indeterminate forms
      The divisions 0/0 and ±∞/±∞
      The multiplications 0×±∞ and ±∞×0
      The additions ∞ + (−∞), (−∞) + ∞ and equivalent subtractions
      The standard has alternative functions for powers:
      The standard pow function and the integer exponent pown function define 0pow(0), 1pow(∞),
      and ∞pow(0) as 1.
      The powr function defines all three indeterminate forms as invalid operations and 
      so returns NaN.
3. Real operations with complex results, for example:
      The square root of a negative number.
      The logarithm of a negative number
      The inverse sine or cosine of a number that is less than −1 or greater than +1.

これで、問題を自分で解決するのに役立ちます。

于 2012-12-24T13:10:03.570 に答える
0

各計算のサブ結果を印刷する必要があります。または、isnan()関数を使用して通常の場所でチェックし、それがどこから来ているかを追跡する必要があります。それはいくつかの「悪い」数学であるか、そもそもゴミを食べているかのどちらかです(初期化されていない変数はNaNのものである可能性があります)

于 2012-12-24T13:17:55.567 に答える