Skip to content

Commit 94cbee9

Browse files
c123wsimonbyrne
authored andcommitted
Add logerfc function (#194)
* Add prototype logerfc function. * Add unit tests (and change src/erf.jl argument types). * Add docstring.
1 parent 5dc9883 commit 94cbee9

File tree

5 files changed

+52
-3
lines changed

5 files changed

+52
-3
lines changed

docs/src/functions_list.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ CurrentModule = SpecialFunctions
88
SpecialFunctions.erf
99
SpecialFunctions.erfc
1010
SpecialFunctions.erfcx
11+
SpecialFunctions.logerfc
1112
SpecialFunctions.erfi
1213
SpecialFunctions.dawson
1314
SpecialFunctions.erfinv

docs/src/functions_overview.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ Here the *Special Functions* are listed according to the structure of [NIST Digi
3737
| [`erfc(x)`](@ref SpecialFunctions.erfc) | complementary error function, i.e. the accurate version of ``1-\operatorname{erf}(x)`` for large ``x`` |
3838
| [`erfcinv(x)`](@ref SpecialFunctions.erfcinv) | inverse function to [`erfc()`](@ref SpecialFunctions.erfc) |
3939
| [`erfcx(x)`](@ref SpecialFunctions.erfcx) | scaled complementary error function, i.e. accurate ``e^{x^2} \operatorname{erfc}(x)`` for large ``x`` |
40+
| [`logerfc(x)`](@ref SpecialFunctions.logerfc) | log of the complementary error function, i.e. accurate ``\operatorname{ln}(\operatorname{erfc}(x))`` for large ``x`` |
4041
| [`erfi(x)`](@ref SpecialFunctions.erfi) | imaginary error function defined as ``-i \operatorname{erf}(ix)`` |
4142
| [`erfinv(x)`](@ref SpecialFunctions.erfinv) | inverse function to [`erf()`](@ref SpecialFunctions.erf) |
4243
| [`dawson(x)`](@ref SpecialFunctions.dawson) | scaled imaginary error function, a.k.a. Dawson function, i.e. accurate ``\frac{\sqrt{\pi}}{2} e^{-x^2} \operatorname{erfi}(x)`` for large ``x`` |

src/SpecialFunctions.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export
3434
erfcx,
3535
erfi,
3636
erfinv,
37+
logerfc,
3738
eta,
3839
digamma,
3940
invdigamma,
@@ -64,7 +65,7 @@ include("betanc.jl")
6465
include("beta_inc.jl")
6566
include("deprecated.jl")
6667

67-
for f in (:digamma, :erf, :erfc, :erfcinv, :erfcx, :erfi, :erfinv,
68+
for f in (:digamma, :erf, :erfc, :erfcinv, :erfcx, :erfi, :erfinv, :logerfc,
6869
:eta, :gamma, :invdigamma, :logfactorial, :lgamma, :trigamma, :ellipk, :ellipe)
6970
@eval $(f)(::Missing) = missing
7071
end

src/erf.jl

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,3 +426,36 @@ function erfcx(x::BigFloat)
426426
return (1+s)/(x*sqrt(oftype(x,pi)))
427427
end
428428
end
429+
430+
@doc raw"""
431+
logerfc(x)
432+
433+
Compute the natural logarithm of the complementary error function of ``x``, that is
434+
435+
```math
436+
\operatorname{logerfc}(x) = \operatorname{ln}(\operatorname{erfc}(x))
437+
\quad \text{for} \quad x \in \mathbb{R} \, .
438+
```
439+
440+
This is the accurate version of ``\operatorname{ln}(\operatorname{erfc}(x))`` for large ``x``.
441+
442+
External links:
443+
[Wikipedia](https://en.wikipedia.org/wiki/Error_function).
444+
445+
See also: [`erfcx(x)`](@ref SpecialFunctions.erfcx).
446+
447+
# Implementation
448+
Based on the [`erfc(x)`](@ref SpecialFunctions.erfc) and [`erfcx(x)`](@ref SpecialFunctions.erfcx) functions.
449+
Currently only implemented for `Float32`, `Float64`, and `BigFloat`.
450+
"""
451+
function logerfc(x::Union{Float32, Float64, BigFloat})
452+
# Don't include Float16 in the Union, otherwise logerfc would currently work for x <= 0.0, but not x > 0.0
453+
if x > 0.0
454+
return log(erfcx(x)) - x^2
455+
else
456+
return log(erfc(x))
457+
end
458+
end
459+
460+
logerfc(x::Real) = logerfc(float(x))
461+
logerfc(x::AbstractFloat) = throw(MethodError(logerfc, x))

test/erf.jl

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,18 @@
99
@test erfc(Float64(1)) 0.15729920705028513066 rtol=2*eps(Float64)
1010

1111
@test_throws MethodError erfcx(Float16(1))
12-
@test erfcx(Float32(1)) 0.42758357615580700442 rtol=2*eps(Float32)
13-
@test erfcx(Float64(1)) 0.42758357615580700442 rtol=2*eps(Float64)
12+
@test erfcx(Float32(1)) 0.42758357615580700442 rtol=2*eps(Float32)
13+
@test erfcx(Float64(1)) 0.42758357615580700442 rtol=2*eps(Float64)
14+
15+
@test_throws MethodError logerfc(Float16(1))
16+
@test_throws MethodError logerfc(Float16(-1))
17+
@test logerfc(Float32(-100)) 0.6931471805599453 rtol=2*eps(Float32)
18+
@test logerfc(Float64(-100)) 0.6931471805599453 rtol=2*eps(Float64)
19+
@test logerfc(Float32(1000)) -1.0000074801207219e6 rtol=2*eps(Float32)
20+
@test logerfc(Float64(1000)) -1.0000074801207219e6 rtol=2*eps(Float64)
21+
@test logerfc(1000) -1.0000074801207219e6 rtol=2*eps(Float32)
22+
@test logerfc(Float32(10000)) log(erfc(BigFloat(10000, 100))) rtol=2*eps(Float32)
23+
@test logerfc(Float64(10000)) log(erfc(BigFloat(10000, 100))) rtol=2*eps(Float64)
1424

1525
@test_throws MethodError erfi(Float16(1))
1626
@test erfi(Float32(1)) 1.6504257587975428760 rtol=2*eps(Float32)
@@ -69,6 +79,9 @@
6979
@test erfcx(BigFloat(1.8e88)) erfcx(1.8e88) rtol=4*eps()
7080
@test isnan(erfcx(BigFloat(NaN)))
7181

82+
@test logerfc(BigFloat(1000, 100)) -1.0000074801207219e6 rtol=2*eps(Float64)
83+
@test isnan(logerfc(BigFloat(NaN)))
84+
7285
@test_throws MethodError erfi(big(1.0))
7386

7487
@test_throws MethodError erfinv(BigFloat(1))

0 commit comments

Comments
 (0)