我在程序中使用libresample。经过一段时间(大约50分钟),它在一个工作站的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上用gcc 4.1.2编译的。
更新:将变量打印为%a。结果:
//t = 0x1.2806bap+2
//Hp = 0xb3bb870
t = *Hp;
//t = nan
更新2:如果代码由icpc编译,则不存在此类问题。编译器是否有特定问题?
答案 0 :(得分:5)
显然,-0.009034•0.000000不应产生NaN。因此,问题中呈现的代码和数据不是实际计算的准确表示,或者计算实现是有缺陷的。
如果我们假设硬件和基本计算实现没有缺陷,那么调查的一些可能性包括:
t
和*Xp
的记录无法在乘法之前立即记录t
和*Xp
的正确值,或者立即记录正确的t
值在乘法之后。t
或*Xp
的值显示不正确。例如,用于显示*Xp
的格式显示“0.000000”,即使*Xp
具有其他值,例如NaN。Xp
点不合适,导致*Xp
不可靠(例如,由外部操作更改)。注意:使用浮点对象进行调试时,您应不使用“%f”等格式进行打印,尤其是不能使用数字位数的默认值。您应该使用“%a”打印,它使用十六进制表示打印浮点值的确切值。在许多情况下,您也可以使用“%.99g”,前提是您的C实现提供了将浮点值转换为十进制的良好转换。
答案 1 :(得分:3)
第五种可能性 Eric Postpischil 的优秀答案没有提及:
这两个最常见的原因是调用返回浮点结果的函数,这些函数在范围内没有原型(具有许多调用约定,这将导致调用者无法从FP堆栈中弹出结果),和不正确的手写(可能是内联)组装。
故障仅在经过一段时间后才会发生,这为这种可能性提供了一些证据;如果有一个很少使用的代码路径泄漏了浮点堆栈的一个元素,那么在失败清单之前需要多次使用它,这可能让它直到现在才被告知。
要诊断或排除这种可能性,您需要查看浮点状态寄存器(FPSR)的第6位(SF)。根据您使用的编译器,检查FPSR的确切方法可能会有所不同。
答案 2 :(得分:1)
从Wiki开始,有三种操作可以按如下方式返回NaN:
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.
现在这可以帮助您自己解决问题。
答案 3 :(得分:0)
您必须打印每个计算的子结果 - 或使用isnan()
功能检查常规位置,并追踪它的来源。这可能是一些“糟糕”的数学,或者你首先是垃圾(未初始化的变量可能是NaN的)