C中的反向误差函数

时间:2014-12-01 13:20:36

标签: c function inverse

是否可以计算C?

中的逆误差函数

我可以在erf(x)中找到计算错误函数的<math.h>,但我找不到任何可以做反过来的事情。

3 个答案:

答案 0 :(得分:5)

快速&amp;脏,容差低于+ -6e-3。工作基于&#34;误差函数的一个方便的近似及其反函数&#34;作者:Sergei Winitzki。

C / C ++代码:

float myErfInv2(float x){
   float tt1, tt2, lnx, sgn;
   sgn = (x < 0) ? -1.0f : 1.0f;

   x = (1 - x)*(1 + x);        // x = 1 - x*x;
   lnx = logf(x);

   tt1 = 2/(PI*0.147) + 0.5f * lnx;
   tt2 = 1/(0.147) * lnx;

   return(sgn*sqrtf(-tt1 + sqrtf(tt1*tt1 - tt2)));
}

MATLAB健全性检查:

clear all,  close all, clc 
x = linspace(-1, 1,10000); 

a = 0.147;
u = log(1-x.^2);  
u1 = 2/(pi*a) + u/2;    u2 = u/a;
y = sign(x).*sqrt(-u1+sqrt(u1.^2 - u2)); 

f = erfinv(x); axis equal

figure(1);
plot(x, [y;f]);

figure(2);
e = f-y;
plot(x, e);

MATLAB图:

Plot1 Plot2

答案 1 :(得分:3)

目前,ISO C标准数学库不包含erfinv()或其单精度变体erfinvf()。但是,创建一个自己的版本并不困难,我在下面演示了erfinvf()具有合理准确性和性能的实现。

观察graph of the inverse error function我们观察到它是高度非线性的,因此难以用多项式进行近似。处理这种情况的一个策略是通过从简单的基本函数(它本身可以以高性能和高精度计算)和一个相当线性的函数(更容易适应多项式近似或理性)来合成它来“线性化”这样的函数。近似程度低。

以下是从文献中已知的erfinv线性化的一些方法,所有这些方法都基于对数。通常,作者将逆误差函数的主要相当线性部分从零到非常大约0.9的切换点和从切换点到单位的尾部部分区分开来。在下文中,log()表示自然对数,R()表示有理逼近,P()表示多项式逼近。

一个。 J. Strecok,“关于误差函数逆的计算”。 计算数学,Vol。 22,No。101(1968年1月),第144-158页(online)

  

β(x)=(-log(1-x 2 ]))½; erfinv(x)= x·R(x 2 )[main]; R(x)·β(x)[尾]

学家M. Blair,C。A. Edwards,J。H. Johnson,“用于误差函数逆的理性切比雪夫近似”。 计算数学,Vol。 30,No.136(1976年10月),第827-830页(online)

  

ξ=( - log(1-x)); erfinv(x)= x·R(x 2 )[main]; ξ -1 ·R(ξ)[tail]

微米。贾尔斯,“近似erfinv函数。”在 GPU Computing Gems Jade Edition ,第109-116页。 2011. (online)

  

w = -log(1-x 2 ); s =√w; erfinv(x)= x·P(w)[main]; x·P(s)[尾]

下面的解决方案通常遵循Giles的方法,但是在不需要尾部的平方根的情况下简化它,即它使用两个近似类型x·P(w)。该代码最大限度地利用了融合乘法 - 加法运算FMA,它通过C中的标准数学函数fma()fmaf()公开。许多常见的计算平台,如 IBM Power,Arm64,x86-64和GPU在硬件中提供此操作。在不存在硬件支持的情况下,使用fma{f}()可能会使下面的代码变得无法接受,因为操作需要由标准数学库模拟。此外,功能错误的FMA模拟are known to exist

标准数学库的对数函数logf()的准确性会对下面my_erfinvf()的准确性产生一些影响。只要库提供忠实的实现,错误&lt; 1 ulp,规定的错误界限应该保持,并且它对我尝试的几个库做了。为了提高可重复性,我已经包含了我自己的便携式忠实实现,my_logf()

#include <math.h>
float my_logf (float);

/* compute inverse error functions with maximum error of 2.35793 ulp */
float my_erfinvf (float a)
{
    float p, r, t;
    t = fmaf (a, 0.0f - a, 1.0f);
    t = my_logf (t);
    if (fabsf(t) > 6.125f) { // maximum ulp error = 2.35793
        p =              3.03697567e-10f; //  0x1.4deb44p-32 
        p = fmaf (p, t,  2.93243101e-8f); //  0x1.f7c9aep-26 
        p = fmaf (p, t,  1.22150334e-6f); //  0x1.47e512p-20 
        p = fmaf (p, t,  2.84108955e-5f); //  0x1.dca7dep-16 
        p = fmaf (p, t,  3.93552968e-4f); //  0x1.9cab92p-12 
        p = fmaf (p, t,  3.02698812e-3f); //  0x1.8cc0dep-9 
        p = fmaf (p, t,  4.83185798e-3f); //  0x1.3ca920p-8 
        p = fmaf (p, t, -2.64646143e-1f); // -0x1.0eff66p-2 
        p = fmaf (p, t,  8.40016484e-1f); //  0x1.ae16a4p-1 
    } else { // maximum ulp error = 2.35456
        p =              5.43877832e-9f;  //  0x1.75c000p-28 
        p = fmaf (p, t,  1.43286059e-7f); //  0x1.33b458p-23 
        p = fmaf (p, t,  1.22775396e-6f); //  0x1.49929cp-20 
        p = fmaf (p, t,  1.12962631e-7f); //  0x1.e52bbap-24 
        p = fmaf (p, t, -5.61531961e-5f); // -0x1.d70c12p-15 
        p = fmaf (p, t, -1.47697705e-4f); // -0x1.35be9ap-13 
        p = fmaf (p, t,  2.31468701e-3f); //  0x1.2f6402p-9 
        p = fmaf (p, t,  1.15392562e-2f); //  0x1.7a1e4cp-7 
        p = fmaf (p, t, -2.32015476e-1f); // -0x1.db2aeep-3 
        p = fmaf (p, t,  8.86226892e-1f); //  0x1.c5bf88p-1 
    }
    r = a * p;
    return r;
}

/* compute natural logarithm with a maximum error of 0.85089 ulp */
float my_logf (float a)
{
    float i, m, r, s, t;
    int e;

    m = frexpf (a, &e);
    if (m < 0.666666667f) { // 0x1.555556p-1
        m = m + m;
        e = e - 1;
    }
    i = (float)e;
    /* m in [2/3, 4/3] */
    m = m - 1.0f;
    s = m * m;
    /* Compute log1p(m) for m in [-1/3, 1/3] */
    r =             -0.130310059f;  // -0x1.0ae000p-3
    t =              0.140869141f;  //  0x1.208000p-3
    r = fmaf (r, s, -0.121484190f); // -0x1.f19968p-4
    t = fmaf (t, s,  0.139814854f); //  0x1.1e5740p-3
    r = fmaf (r, s, -0.166846052f); // -0x1.55b362p-3
    t = fmaf (t, s,  0.200120345f); //  0x1.99d8b2p-3
    r = fmaf (r, s, -0.249996200f); // -0x1.fffe02p-3
    r = fmaf (t, m, r);
    r = fmaf (r, m,  0.333331972f); //  0x1.5554fap-2
    r = fmaf (r, m, -0.500000000f); // -0x1.000000p-1
    r = fmaf (r, s, m);
    r = fmaf (i,  0.693147182f, r); //  0x1.62e430p-1 // log(2)
    if (!((a > 0.0f) && (a <= 3.40282346e+38f))) { // 0x1.fffffep+127
        r = a + a;  // silence NaNs if necessary
        if (a  < 0.0f) r = ( 0.0f / 0.0f); //  NaN
        if (a == 0.0f) r = (-1.0f / 0.0f); // -Inf
    }
    return r;
}

答案 2 :(得分:1)

我不认为它是<math.h>中的standard实现,但是还有其他C math个库实现了反向错误函数erfinv(x),您可以使用它们