Skip to content

Commit ccceddc

Browse files
committed
Merge pull request #2987 from stevengj/erfinv
add inverse error functions erfinv and erfcinv
2 parents 6b53b1b + 9c24795 commit ccceddc

File tree

5 files changed

+253
-1
lines changed

5 files changed

+253
-1
lines changed

base/exports.jl

+2
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,8 @@ export
318318
erfc,
319319
erfcx,
320320
erfi,
321+
erfinv,
322+
erfcinv,
321323
exp,
322324
exp2,
323325
expm1,

base/math.jl

+212-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ export sin, cos, tan, sinh, cosh, tanh, asin, acos, atan,
1414
airy, airyai, airyprime, airyaiprime, airybi, airybiprime,
1515
besselj0, besselj1, besselj, bessely0, bessely1, bessely,
1616
hankelh1, hankelh2, besseli, besselk, besselh,
17-
beta, lbeta, eta, zeta, digamma
17+
beta, lbeta, eta, zeta, digamma,
18+
erfinv, erfcinv
1819

1920
import Base.log, Base.exp, Base.sin, Base.cos, Base.tan, Base.sinh, Base.cosh,
2021
Base.tanh, Base.asin, Base.acos, Base.atan, Base.asinh, Base.acosh,
@@ -695,4 +696,214 @@ for f in (:erfcx, :erfi, :Dawson)
695696
end
696697
end
697698

699+
# evaluate p[1] + x * (p[2] + x * (....)), i.e. a polynomial via Horner's rule
700+
macro horner(x, p...)
701+
ex = p[end]
702+
for i = length(p)-1:-1:1
703+
ex = :($(p[i]) + $x * $ex)
704+
end
705+
ex
706+
end
707+
708+
# Compute the inverse of the error function: erf(erfinv(x)) == x,
709+
# using the rational approximants tabulated in:
710+
# J. M. Blair, C. A. Edwards, and J. H. Johnson, "Rational Chebyshev
711+
# approximations for the inverse of the error function," Math. Comp. 30,
712+
# pp. 827--830 (1976).
713+
# http://dx.doi.org/10.1090/S0025-5718-1976-0421040-7
714+
# http://www.jstor.org/stable/2005402
715+
function erfinv(x::Float64)
716+
a = abs(x)
717+
if a >= 1.0
718+
if x == 1.0
719+
return inf(Float64)
720+
elseif x == -1.0
721+
return -inf(Float64)
722+
end
723+
throw(DomainError())
724+
elseif a <= 0.75 # Table 17 in Blair et al.
725+
t = x*x - 0.5625
726+
return x * @horner(t, 0.16030_49558_44066_229311e2,
727+
-0.90784_95926_29603_26650e2,
728+
0.18644_91486_16209_87391e3,
729+
-0.16900_14273_46423_82420e3,
730+
0.65454_66284_79448_7048e2,
731+
-0.86421_30115_87247_794e1,
732+
0.17605_87821_39059_0) /
733+
@horner(t, 0.14780_64707_15138_316110e2,
734+
-0.91374_16702_42603_13936e2,
735+
0.21015_79048_62053_17714e3,
736+
-0.22210_25412_18551_32366e3,
737+
0.10760_45391_60551_23830e3,
738+
-0.20601_07303_28265_443e2,
739+
0.1e1)
740+
elseif a <= 0.9375 # Table 37 in Blair et al.
741+
t = x*x - 0.87890625
742+
return x * @horner(t, -0.15238_92634_40726_128e-1,
743+
0.34445_56924_13612_5216,
744+
-0.29344_39867_25424_78687e1,
745+
0.11763_50570_52178_27302e2,
746+
-0.22655_29282_31011_04193e2,
747+
0.19121_33439_65803_30163e2,
748+
-0.54789_27619_59831_8769e1,
749+
0.23751_66890_24448) /
750+
@horner(t, -0.10846_51696_02059_954e-1,
751+
0.26106_28885_84307_8511,
752+
-0.24068_31810_43937_57995e1,
753+
0.10695_12997_33870_14469e2,
754+
-0.23716_71552_15965_81025e2,
755+
0.24640_15894_39172_84883e2,
756+
-0.10014_37634_97830_70835e2,
757+
0.1e1)
758+
else # Table 57 in Blair et al.
759+
t = 1.0 / sqrt(-log(1.0 - a))
760+
return @horner(t, 0.10501_31152_37334_38116e-3,
761+
0.10532_61131_42333_38164_25e-1,
762+
0.26987_80273_62432_83544_516,
763+
0.23268_69578_89196_90806_414e1,
764+
0.71678_54794_91079_96810_001e1,
765+
0.85475_61182_21678_27825_185e1,
766+
0.68738_08807_35438_39802_913e1,
767+
0.36270_02483_09587_08930_02e1,
768+
0.88606_27392_96515_46814_9) /
769+
(copysign(t, x) *
770+
@horner(t, 0.10501_26668_70303_37690e-3,
771+
0.10532_86230_09333_27531_11e-1,
772+
0.27019_86237_37515_54845_553,
773+
0.23501_43639_79702_53259_123e1,
774+
0.76078_02878_58012_77064_351e1,
775+
0.11181_58610_40569_07827_3451e2,
776+
0.11948_78791_84353_96667_8438e2,
777+
0.81922_40974_72699_07893_913e1,
778+
0.40993_87907_63680_15361_45e1,
779+
0.1e1))
780+
end
781+
end
782+
783+
function erfinv(x::Float32)
784+
a = abs(x)
785+
if a >= 1.0f0
786+
if x == 1.0f0
787+
return inf(Float64)
788+
elseif x == -1.0f0
789+
return -inf(Float64)
790+
end
791+
throw(DomainError())
792+
elseif a <= 0.75f0 # Table 10 in Blair et al.
793+
t = x*x - 0.5625f0
794+
return x * @horner(t, -0.13095_99674_22f2,
795+
0.26785_22576_0f2,
796+
-0.92890_57365f1) /
797+
@horner(t, -0.12074_94262_97f2,
798+
0.30960_61452_9f2,
799+
-0.17149_97799_1f2,
800+
0.1f1)
801+
elseif a <= 0.9375f0 # Table 29 in Blair et al.
802+
t = x*x - 0.87890625f0
803+
return x * @horner(t, -0.12402_56522_1f0,
804+
0.10688_05957_4f1,
805+
-0.19594_55607_8f1,
806+
0.42305_81357f0) /
807+
@horner(t, -0.88276_97997f-1,
808+
0.89007_43359f0,
809+
-0.21757_03119_6f1,
810+
0.1f1)
811+
else # Table 50 in Blair et al.
812+
t = 1.0f0 / sqrt(-log(1.0f0 - a))
813+
return @horner(t, 0.15504_70003_116f0,
814+
0.13827_19649_631f1,
815+
0.69096_93488_87f0,
816+
-0.11280_81391_617f1,
817+
0.68054_42468_25f0,
818+
-0.16444_15679_1f0) /
819+
(copysign(t, x) *
820+
@horner(t, 0.15502_48498_22f0,
821+
0.13852_28141_995f1,
822+
0.1f1))
823+
end
824+
end
825+
826+
erfinv(x::Integer) = erfinv(float(x))
827+
@vectorize_1arg Real erfinv
828+
829+
# Inverse complementary error function: use Blair tables for y = 1-x,
830+
# exploiting the greater accuracy of y (vs. x) when y is small.
831+
function erfcinv(y::Float64)
832+
if y > 0.0625
833+
return erfinv(1.0 - y)
834+
elseif y <= 0.0
835+
if y == 0.0
836+
return inf(Float64)
837+
end
838+
throw(DomainError())
839+
elseif y >= 1e-100 # Table 57 in Blair et al.
840+
t = 1.0 / sqrt(-log(y))
841+
return @horner(t, 0.10501_31152_37334_38116e-3,
842+
0.10532_61131_42333_38164_25e-1,
843+
0.26987_80273_62432_83544_516,
844+
0.23268_69578_89196_90806_414e1,
845+
0.71678_54794_91079_96810_001e1,
846+
0.85475_61182_21678_27825_185e1,
847+
0.68738_08807_35438_39802_913e1,
848+
0.36270_02483_09587_08930_02e1,
849+
0.88606_27392_96515_46814_9) /
850+
(t *
851+
@horner(t, 0.10501_26668_70303_37690e-3,
852+
0.10532_86230_09333_27531_11e-1,
853+
0.27019_86237_37515_54845_553,
854+
0.23501_43639_79702_53259_123e1,
855+
0.76078_02878_58012_77064_351e1,
856+
0.11181_58610_40569_07827_3451e2,
857+
0.11948_78791_84353_96667_8438e2,
858+
0.81922_40974_72699_07893_913e1,
859+
0.40993_87907_63680_15361_45e1,
860+
0.1e1))
861+
else # Table 80 in Blair et al.
862+
t = 1.0 / sqrt(-log(y))
863+
return @horner(t, 0.34654_29858_80863_50177e-9,
864+
0.25084_67920_24075_70520_55e-6,
865+
0.47378_13196_37286_02986_534e-4,
866+
0.31312_60375_97786_96408_3388e-2,
867+
0.77948_76454_41435_36994_854e-1,
868+
0.70045_68123_35816_43868_271e0,
869+
0.18710_42034_21679_31668_683e1,
870+
0.71452_54774_31351_45428_3e0) /
871+
(t * @horner(t, 0.34654_29567_31595_11156e-9,
872+
0.25084_69079_75880_27114_87e-6,
873+
0.47379_53129_59749_13536_339e-4,
874+
0.31320_63536_46177_68848_0813e-2,
875+
0.78073_48906_27648_97214_733e-1,
876+
0.70715_04479_95337_58619_993e0,
877+
0.19998_51543_49112_15105_214e1,
878+
0.15072_90269_27316_80008_56e1,
879+
0.1e1))
880+
end
881+
end
882+
883+
function erfcinv(y::Float32)
884+
if y > 0.0625f0
885+
return erfinv(1.0f0 - y)
886+
elseif y <= 0.0f0
887+
if y == 0.0f0
888+
return inf(Float32)
889+
end
890+
throw(DomainError())
891+
else # Table 50 in Blair et al.
892+
t = 1.0f0 / sqrt(-log(y))
893+
return @horner(t, 0.15504_70003_116f0,
894+
0.13827_19649_631f1,
895+
0.69096_93488_87f0,
896+
-0.11280_81391_617f1,
897+
0.68054_42468_25f0,
898+
-0.16444_15679_1f0) /
899+
(t *
900+
@horner(t, 0.15502_48498_22f0,
901+
0.13852_28141_995f1,
902+
0.1f1))
903+
end
904+
end
905+
906+
erfcinv(x::Integer) = erfcinv(float(x))
907+
@vectorize_1arg Real erfcinv
908+
698909
end # module

doc/helpdb.jl

+16
Original file line numberDiff line numberDiff line change
@@ -2695,6 +2695,22 @@ FloatingPoint
26952695
26962696
"),
26972697

2698+
("Mathematical Functions","Base","erfinv","erfinv(x)
2699+
2700+
2701+
Compute the inverse error function of a real \"x\",
2702+
so that \\operatorname{erf}(\\operatorname{erfinv}(x)) = x.
2703+
2704+
"),
2705+
2706+
("Mathematical Functions","Base","erfcinv","erfcinv(x)
2707+
2708+
2709+
Compute the inverse complementary error function of a real \"x\",
2710+
so that \\operatorname{erfc}(\\operatorname{erfcinv}(x)) = x.
2711+
2712+
"),
2713+
26982714
("Mathematical Functions","Base","dawson","dawson(x)
26992715
27002716

doc/stdlib/base.rst

+10
Original file line numberDiff line numberDiff line change
@@ -1527,6 +1527,16 @@ Mathematical Functions
15271527
Compute the Dawson function (scaled imaginary error function) of ``x``,
15281528
defined by :math:`\frac{\sqrt{\pi}}{2} e^{-x^2} \operatorname{erfi}(x)`.
15291529

1530+
.. function:: erfinv(x)
1531+
1532+
Compute the inverse error function of a real ``x``,
1533+
defined by :math:`\operatorname{erf}(\operatorname{erfinv}(x)) = x`.
1534+
1535+
.. function:: erfcinv(x)
1536+
1537+
Compute the inverse error complementary function of a real ``x``,
1538+
defined by :math:`\operatorname{erfc}(\operatorname{erfcinv}(x)) = x`.
1539+
15301540
.. function:: real(z)
15311541

15321542
Return the real part of the complex number ``z``

test/math.jl

+13
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
@test_approx_eq erfc(1) 0.15729920705028513066
1010
@test_approx_eq erfcx(1) 0.42758357615580700442
1111
@test_approx_eq erfi(1) 1.6504257587975428760
12+
@test_approx_eq erfinv(0.84270079294971486934) 1
13+
@test_approx_eq erfcinv(0.15729920705028513066) 1
1214
@test_approx_eq dawson(1) 0.53807950691276841914
1315
# TODO: complex versions only supported on 64-bit for now
1416
if WORD_SIZE==64
@@ -18,6 +20,17 @@ if WORD_SIZE==64
1820
@test_approx_eq erfi(1+2im) -0.011259006028815025076+1.0036063427256517509im
1921
@test_approx_eq dawson(1+2im) -13.388927316482919244-11.828715103889593303im
2022
end
23+
for x in logspace(-200, -0.01)
24+
@test_approx_eq_eps erf(erfinv(x)) x 1e-12*x
25+
@test_approx_eq_eps erf(erfinv(-x)) -x 1e-12*x
26+
@test_approx_eq_eps erfc(erfcinv(2*x)) 2*x 1e-12*x
27+
if x > 1e-20
28+
xf = float32(x)
29+
@test_approx_eq_eps erf(erfinv(xf)) xf 1e-5*xf
30+
@test_approx_eq_eps erf(erfinv(-xf)) -xf 1e-5*xf
31+
@test_approx_eq_eps erfc(erfcinv(2xf)) 2xf 1e-5*xf
32+
end
33+
end
2134

2235
# airy
2336
@test_approx_eq airy(1.8) 0.0470362168668458052247

0 commit comments

Comments
 (0)