Skip to content

Commit e868e46

Browse files
authored
bigfloat erfinv and erfcinv (#278)
1 parent 9207729 commit e868e46

File tree

2 files changed

+81
-22
lines changed

2 files changed

+81
-22
lines changed

src/erf.jl

Lines changed: 66 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,9 @@ Accurate version of `erf(y) - erf(x)` (for real arguments only).
5757
5858
External links: [DLMF](https://dlmf.nist.gov/7.2.1), [Wikipedia](https://en.wikipedia.org/wiki/Error_function).
5959
60-
See also: [`erfc(x)`](@ref SpecialFunctions.erfc), [`erfcx(x)`](@ref SpecialFunctions.erfcx),
61-
[`erfi(x)`](@ref SpecialFunctions.erfi), [`dawson(x)`](@ref SpecialFunctions.dawson),
62-
[`erfinv(x)`](@ref SpecialFunctions.erfinv), [`erfcinv(x)`](@ref SpecialFunctions.erfcinv).
60+
See also: [`erfc(x)`](@ref erfc), [`erfcx(x)`](@ref erfcx),
61+
[`erfi(x)`](@ref erfi), [`dawson(x)`](@ref dawson),
62+
[`erfinv(x)`](@ref erfinv), [`erfcinv(x)`](@ref erfcinv).
6363
6464
# Implementation by
6565
- `Float32`/`Float64`: C standard math library
@@ -97,7 +97,7 @@ This is the accurate version of `1-erf(x)` for large ``x``.
9797
External links: [DLMF](https://dlmf.nist.gov/7.2.2),
9898
[Wikipedia](https://en.wikipedia.org/wiki/Error_function#Complementary_error_function).
9999
100-
See also: [`erf(x)`](@ref SpecialFunctions.erf).
100+
See also: [`erf(x)`](@ref erf).
101101
102102
# Implementation by
103103
- `Float32`/`Float64`: C standard math library
@@ -123,7 +123,7 @@ Note also that ``\operatorname{erfcx}(-ix)`` computes the Faddeeva function `w(x
123123
External links: [DLMF](https://dlmf.nist.gov/7.2.3),
124124
[Wikipedia](https://en.wikipedia.org/wiki/Error_function#Complementary_error_function).
125125
126-
See also: [`erfc(x)`](@ref SpecialFunctions.erfc).
126+
See also: [`erfc(x)`](@ref erfc).
127127
128128
# Implementation by
129129
- `Float32`/`Float64`: C standard math library
@@ -147,7 +147,7 @@ Compute the imaginary error function of ``x``, defined by
147147
External links:
148148
[Wikipedia](https://en.wikipedia.org/wiki/Error_function#Imaginary_error_function).
149149
150-
See also: [`erf(x)`](@ref SpecialFunctions.erf).
150+
See also: [`erf(x)`](@ref erf).
151151
152152
# Implementation by
153153
- `Float32`/`Float64`: C standard math library
@@ -172,7 +172,7 @@ for large ``x``.
172172
External links: [DLMF](https://dlmf.nist.gov/7.2.5),
173173
[Wikipedia](https://en.wikipedia.org/wiki/Dawson_function).
174174
175-
See also: [`erfi(x)`](@ref SpecialFunctions.erfi).
175+
See also: [`erfi(x)`](@ref erfi).
176176
177177
# Implementation by
178178
- `Float32`/`Float64`: C standard math library
@@ -193,7 +193,7 @@ Compute the inverse error function of a real ``x``, that is
193193
External links:
194194
[Wikipedia](https://en.wikipedia.org/wiki/Error_function#Inverse_functions).
195195
196-
See also: [`erf(x)`](@ref SpecialFunctions.erf).
196+
See also: [`erf(x)`](@ref erf).
197197
198198
# Implementation
199199
Using the rational approximants tabulated in:
@@ -202,6 +202,7 @@ Using the rational approximants tabulated in:
202202
> Math. Comp. 30, pp. 827--830 (1976).
203203
> <https://doi.org/10.1090/S0025-5718-1976-0421040-7>,
204204
> <http://www.jstor.org/stable/2005402>
205+
combined with Newton iterations for `BigFloat`.
205206
"""
206207
function erfinv(x::Float64)
207208
a = abs(x)
@@ -314,7 +315,7 @@ function erfinv(x::Float32)
314315
end
315316
end
316317

317-
erfinv(x::Integer) = erfinv(float(x))
318+
erfinv(x::Union{Integer,Rational}) = erfinv(float(x))
318319

319320
@doc raw"""
320321
erfcinv(x)
@@ -329,7 +330,7 @@ Compute the inverse error complementary function of a real ``x``, that is
329330
External links:
330331
[Wikipedia](https://en.wikipedia.org/wiki/Error_function#Inverse_functions).
331332
332-
See also: [`erfc(x)`](@ref SpecialFunctions.erfc).
333+
See also: [`erfc(x)`](@ref erfc).
333334
334335
# Implementation
335336
Using the rational approximants tabulated in:
@@ -338,6 +339,7 @@ Using the rational approximants tabulated in:
338339
> Math. Comp. 30, pp. 827--830 (1976).
339340
> <https://doi.org/10.1090/S0025-5718-1976-0421040-7>,
340341
> <http://www.jstor.org/stable/2005402>
342+
combined with Newton iterations for `BigFloat`.
341343
"""
342344
function erfcinv(y::Float64)
343345
if y > 0.0625
@@ -413,7 +415,55 @@ function erfcinv(y::Float32)
413415
end
414416
end
415417

416-
erfcinv(x::Integer) = erfcinv(float(x))
418+
function erfinv(y::BigFloat)
419+
xfloat = erfinv(Float64(y))
420+
sqrtπ = sqrt(big(pi))
421+
if isfinite(xfloat)
422+
x = BigFloat(xfloat)
423+
else
424+
# Float64 overflowed, use asymptotic estimate instead
425+
# from erfc(x) ≈ exp(-x²)/x√π ≈ y ⟹ -log(yπ) ≈ x² + log(x) ≈ x²
426+
x = copysign(sqrt(-log((1-abs(y))*sqrtπ)), y)
427+
end
428+
isfinite(x) || return x
429+
sqrtπhalf = sqrtπ * 0.5
430+
tol = 2eps(abs(x))
431+
while true # Newton iterations
432+
Δx = sqrtπhalf * (erf(x) - y) * exp(x^2)
433+
x -= Δx
434+
abs(Δx) < tol && break
435+
end
436+
return x
437+
end
438+
439+
function erfcinv(y::BigFloat)
440+
yfloat = Float64(y)
441+
xfloat = erfcinv(yfloat)
442+
sqrtπ = sqrt(big(pi))
443+
if isfinite(xfloat)
444+
x = BigFloat(xfloat)
445+
else
446+
# Float64 overflowed, use asymptotic estimate instead
447+
# from erfc(x) ≈ exp(-x²)/x√π ≈ y ⟹ -log(yπ) ≈ x² + log(x) ≈ x²
448+
if yfloat < 1
449+
x = sqrt(-log(y*sqrtπ))
450+
else # y must be close to 2
451+
@show x = -sqrt(-log((2-y)*sqrtπ))
452+
end
453+
# TODO: Newton convergence is slow near y=0 singularity; accelerate?
454+
end
455+
isfinite(x) || return x
456+
sqrtπhalf = sqrtπ * 0.5
457+
tol = 2eps(abs(x))
458+
while true # Newton iterations
459+
Δx = sqrtπhalf * (erfc(x) - y) * exp(x^2)
460+
x += Δx
461+
abs(Δx) < tol && break
462+
end
463+
return x
464+
end
465+
466+
erfcinv(x::Union{Integer,Rational}) = erfcinv(float(x))
417467

418468
# MPFR has an open TODO item for this function
419469
# until then, we use [DLMF 7.12.1](https://dlmf.nist.gov/7.12.1) for the tail
@@ -457,10 +507,10 @@ This is the accurate version of ``\operatorname{ln}(\operatorname{erfc}(x))`` fo
457507
458508
External links: [Wikipedia](https://en.wikipedia.org/wiki/Error_function).
459509
460-
See also: [`erfcx(x)`](@ref SpecialFunctions.erfcx).
510+
See also: [`erfcx(x)`](@ref erfcx).
461511
462512
# Implementation
463-
Based on the [`erfc(x)`](@ref SpecialFunctions.erfc) and [`erfcx(x)`](@ref SpecialFunctions.erfcx) functions.
513+
Based on the [`erfc(x)`](@ref erfc) and [`erfcx(x)`](@ref erfcx) functions.
464514
Currently only implemented for `Float32`, `Float64`, and `BigFloat`.
465515
"""
466516
function logerfc(x::Union{Float32, Float64, BigFloat})
@@ -489,10 +539,10 @@ This is the accurate version of ``\operatorname{ln}(\operatorname{erfcx}(x))`` f
489539
490540
External links: [Wikipedia](https://en.wikipedia.org/wiki/Error_function).
491541
492-
See also: [`erfcx(x)`](@ref SpecialFunctions.erfcx).
542+
See also: [`erfcx(x)`](@ref erfcx).
493543
494544
# Implementation
495-
Based on the [`erfc(x)`](@ref SpecialFunctions.erfc) and [`erfcx(x)`](@ref SpecialFunctions.erfcx) functions.
545+
Based on the [`erfc(x)`](@ref erfc) and [`erfcx(x)`](@ref erfcx) functions.
496546
Currently only implemented for `Float32`, `Float64`, and `BigFloat`.
497547
"""
498548
function logerfcx(x::Union{Float32, Float64, BigFloat})
@@ -515,7 +565,7 @@ Compute the natural logarithm of two-argument error function. This is an accurat
515565
516566
External links: [Wikipedia](https://en.wikipedia.org/wiki/Error_function).
517567
518-
See also: [`erf(x,y)`](@ref SpecialFunctions.erf).
568+
See also: [`erf(x,y)`](@ref erf).
519569
"""
520570
function logerf(a::Real, b::Real)
521571
if abs(a) 1/√2 && abs(b) 1/√2

test/erf.jl

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,12 @@
4242
@test erfi(Float32(1)) 1.6504257587975428760 rtol=2*eps(Float32)
4343
@test erfi(Float64(1)) 1.6504257587975428760 rtol=2*eps(Float64)
4444

45-
@test erfinv(Integer(0)) == 0
45+
@test erfinv(Integer(0)) == 0 == erfinv(0//1)
4646
@test_throws MethodError erfinv(Float16(1))
4747
@test erfinv(Float32(0.84270079294971486934)) 1 rtol=2*eps(Float32)
4848
@test erfinv(Float64(0.84270079294971486934)) 1 rtol=2*eps(Float64)
4949

50-
@test erfcinv(Integer(1)) == 0
50+
@test erfcinv(Integer(1)) == 0 == erfcinv(1//1)
5151
@test_throws MethodError erfcinv(Float16(1))
5252
@test erfcinv(Float32(0.15729920705028513066)) 1 rtol=2*eps(Float32)
5353
@test erfcinv(Float64(0.15729920705028513066)) 1 rtol=2*eps(Float64)
@@ -100,11 +100,20 @@
100100

101101
@test_throws MethodError erfi(big(1.0))
102102

103-
@test_throws MethodError erfinv(BigFloat(1))
104-
105-
@test_throws MethodError erfcinv(BigFloat(1))
106-
107103
@test_throws MethodError dawson(BigFloat(1))
104+
105+
for y in (big"1e-1000", big"1e-60", big"0.1", big"0.5", big"1.0", 1+big"1e-50", big"1.2", 2-big"1e-50")
106+
@test erfc(erfcinv(y)) y
107+
end
108+
for y in (big"1e-1000", big"1e-60", big"0.1", big"0.5", 1-big"1e-50")
109+
@test erf(erfinv(y)) y
110+
@test erf(erfinv(-y)) -y
111+
end
112+
@test erfcinv(big(0)) == -erfcinv(big(2)) == erfinv(big(1)) == -erfinv(big(-1)) == Inf
113+
for x in (1+big"1e-3", -1-big"1e-3")
114+
@test_throws DomainError erfinv(x)
115+
@test_throws DomainError erfcinv(1-x)
116+
end
108117
end
109118

110119
@testset "inverse" begin

0 commit comments

Comments
 (0)