是否可以计算C?
中的逆误差函数我可以在erf(x)
中找到计算错误函数的<math.h>
,但我找不到任何可以做反过来的事情。
答案 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图:
答案 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)