![](/img/trans.png)
[英]Why are all my double precision calculations in a loop give the same results?
[英]GNU MPFR give me exact same results on precision higher than 64
我正在尝试对 IIR 精度进行一些研究,因此我使用 GNU MPFR 编写了一个简单的 IIR:
#include <mpfr.h>
class DirectFormIIR
{
public:
DirectFormIIR( int precision );
~DirectFormIIR();
void setParams( double b0, double b1, double b2, double a1, double a2 );
void resetState();
double process( double x0 );
void process( mpfr_t output, mpfr_t input );
private:
void _process();
mpfr_t x0;
mpfr_t x1;
mpfr_t x2;
mpfr_t y0;
mpfr_t y1;
mpfr_t y2;
mpfr_t b0;
mpfr_t b1;
mpfr_t b2;
mpfr_t a1;
mpfr_t a2;
mpfr_t tmp;
};
DirectFormIIR::DirectFormIIR( int precision )
{
mpfr_inits2( precision, x0, x1, x2, y0, y1, y2, b0, b1, b2, a1, a2, tmp, NULL );
}
DirectFormIIR::~DirectFormIIR()
{
mpfr_clears( x0, x1, x2, y0, y1, y2, b0, b1, b2, a1, a2, tmp, NULL );
}
void DirectFormIIR::setParams( double b0, double b1, double b2, double a1, double a2 )
{
mpfr_set_d( this->b0, b0, MPFR_RNDN );
mpfr_set_d( this->b1, b1, MPFR_RNDN );
mpfr_set_d( this->b2, b2, MPFR_RNDN );
mpfr_set_d( this->a1, a1, MPFR_RNDN );
mpfr_set_d( this->a2, a2, MPFR_RNDN );
}
void DirectFormIIR::resetState()
{
mpfr_set_zero( this->x0, 0 );
mpfr_set_zero( this->x1, 0 );
mpfr_set_zero( this->x2, 0 );
mpfr_set_zero( this->y0, 0 );
mpfr_set_zero( this->y1, 0 );
mpfr_set_zero( this->y2, 0 );
}
void DirectFormIIR::process( mpfr_t output, mpfr_t input )
{
mpfr_set( x0, input, MPFR_RNDN );
_process();
mpfr_set( output, y0, MPFR_RNDN );
}
void DirectFormIIR::_process()
{
// calculate current Y
mpfr_mul( tmp, b0, x0, MPFR_RNDN );
mpfr_set( y0, tmp, MPFR_RNDN );
mpfr_mul( tmp, b1, x1, MPFR_RNDN );
mpfr_add( y0, y0, tmp, MPFR_RNDN );
mpfr_mul( tmp, b2, x2, MPFR_RNDN );
mpfr_add( y0, y0, tmp, MPFR_RNDN );
mpfr_mul( tmp, a1, y1, MPFR_RNDN );
mpfr_sub( y0, y0, tmp, MPFR_RNDN );
mpfr_mul( tmp, a2, y2, MPFR_RNDN );
mpfr_sub( y0, y0, tmp, MPFR_RNDN );
// update history
mpfr_set( y2, y1, MPFR_RNDN );
mpfr_set( y1, y0, MPFR_RNDN );
mpfr_set( x2, x1, MPFR_RNDN );
mpfr_set( x1, x0, MPFR_RNDN );
}
但是,超过 64 位精度的计算结果总是给我完全相同的结果。 我很确定我的参数工作正常,因为它产生了正确的均衡器效果,并且在 32 位和更高的输出上存在差异。
下面的代码是运行 IIR 的地方,注意两个 IIR 的差异被放大了 100000 倍以避免消失:
int prec_high = std::max( prec1, prec2 );
mpfr_t x, yprec1, yprec2, ydiff;
mpfr_inits2( prec_high, x, yprec1, yprec2, ydiff, nullptr );
DirectFormIIR iir1( prec1 );
DirectFormIIR iir2( prec2 );
iir1.setParams( b0, b1, b2, a1, a2 );
iir2.setParams( b0, b1, b2, a1, a2 );
for ( int ch = 0; ch < reader->numChannels; ch++ )
{
iir1.resetState();
iir2.resetState();
for ( int i = 0; i < audio_data.getNumSamples(); i++ )
{
mpfr_set_flt( x, audio_data.getSample( ch, i ), MPFR_RNDN );
iir1.process( yprec1, x );
iir2.process( yprec2, x );
mpfr_sub( ydiff, yprec1, yprec2, MPFR_RNDN );
mpfr_set_d( x, 100000.0, MPFR_RNDN );
mpfr_mul( ydiff, ydiff, x, MPFR_RNDN );
audio_data.setSample( ch, i, mpfr_get_flt( ydiff, MPFR_RNDN ) );
}
}
您获得的效果通常是由于程序中某处使用了double
而不应使用的事实。
在您的情况下,您的输入是double
类型的数字,即具有固定的 53 位精度。 因此,计算结果的准确性将因此受到限制。 只有在这些计算中引入了显着的额外错误时,提高计算的精度才会有所帮助,但这里似乎并非如此。
因此,如果您想要更准确的结果,您需要提高输入的准确性(通过选择允许比 53 位更高的精度的类型并确保它们确实更准确)。
我没有为差值设置足够的缩放比例。 在我将其从 100000 增加到 1e15 后,差异变得明显。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.