繁体   English   中英

GNU MPFR 在精度高于 64 时给我完全相同的结果

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM