Skip to content

Commit a6edb57

Browse files
authored
add expinti function (#257)
* add expinti function * expinti tests * more tests * tweak tolerance * test coverage for expinti(::Real)
1 parent e3d55b1 commit a6edb57

File tree

5 files changed

+101
-4
lines changed

5 files changed

+101
-4
lines changed

docs/src/functions_list.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ SpecialFunctions.erfi
1414
SpecialFunctions.dawson
1515
SpecialFunctions.erfinv
1616
SpecialFunctions.erfcinv
17+
SpecialFunctions.expint
18+
SpecialFunctions.expinti
1719
SpecialFunctions.sinint
1820
SpecialFunctions.cosint
1921
SpecialFunctions.digamma
@@ -63,5 +65,4 @@ SpecialFunctions.beta
6365
SpecialFunctions.logbeta
6466
SpecialFunctions.logabsbeta
6567
SpecialFunctions.logabsbinomial
66-
SpecialFunctions.expint
6768
```

docs/src/functions_overview.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ Here the *Special Functions* are listed according to the structure of [NIST Digi
2626
## [Exponential and Trigonometric Integrals](https://dlmf.nist.gov/6)
2727
| Function | Description |
2828
|:-------- |:----------- |
29-
| [`expint(nu, z)`](@ref SpecialFunctions.expint) | [exponential integral](https://en.wikipedia.org/wiki/Exponential_integral) ``E_\nu(z)`` |
29+
| [`expint(ν, z)`](@ref SpecialFunctions.expint) | [exponential integral](https://en.wikipedia.org/wiki/Exponential_integral) ``E_\nu(z)`` |
30+
| [`expinti(x)`](@ref SpecialFunctions.expint) | [exponential integral](https://en.wikipedia.org/wiki/Exponential_integral) ``\operatorname{Ei}(z)`` |
3031
| [`sinint(x)`](@ref SpecialFunctions.sinint) | [sine integral](https://en.wikipedia.org/wiki/Trigonometric_integral#Sine_integral) ``Si(x)`` |
3132
| [`cosint(x)`](@ref SpecialFunctions.cosint) | [cosine integral](https://en.wikipedia.org/wiki/Trigonometric_integral#Cosine_integral) ``Ci(x)`` |
3233

src/SpecialFunctions.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,11 @@ export
5555
hankelh2,
5656
hankelh2x,
5757
zeta,
58+
expint,
59+
expinti,
5860
sinint,
5961
cosint,
60-
lbinomial,
61-
expint
62+
lbinomial
6263

6364
include("bessel.jl")
6465
include("erf.jl")

src/expint.jl

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,3 +432,67 @@ function expint(ν::Number, z::Number, niter::Int=1000)
432432
end
433433
throw("unreachable")
434434
end
435+
436+
##############################################################################
437+
# expinti function Ei
438+
439+
"""
440+
expinti(x::Real)
441+
442+
Computes the exponential integral function ``\\operatorname{Ei}(x) = \\int_{-\\infty}^x \\frac{e^t}{t} dt``,
443+
which is equivalent to ``-\\Re[E_1(-x)]`` where ``E_1`` is the `expint` function.
444+
"""
445+
expinti(x::Real) = x 0 ? -expint(-x) : -real(expint(complex(-x)))
446+
447+
# inline the Taylor expansion of Ei for a given order n, in double precision
448+
macro Ei_taylor64(z, n::Integer)
449+
c = .- E₁_taylor_coefficients(Float64, n) .* (-1).^(0:n)
450+
zesc = esc(z)
451+
taylor = :(@evalpoly $zesc)
452+
append!(taylor.args, c)
453+
:( $taylor + log($zesc) )
454+
end
455+
456+
# optimized double-precision version
457+
function expinti(x::Float64)
458+
x 0 && return -expint(-x)
459+
460+
if x > 2.15
461+
# minimax rational approximants:
462+
x < 4 && return @evalpoly(x,-2.43791466332154621,3.09402100064798205,9.35202477109609955,0.152659977028953397,0.0157273683896079142,0.0345566671015011426,-0.000421531433157416203) /
463+
@evalpoly(x,1.0,4.28055563991564399,0.537599625698465573,-0.511064414527643313,0.0867748262262250088,-0.00623913330836521800,0.000172066498182538260)
464+
x < 10 && return exp(x) *
465+
@evalpoly(x,-1.58447018083420958,4.71806833998906997,-0.587691572500210206,0.125012472861504555,-0.00178055441724967428,0.000633648975971195928,0.0000147213934578379204,2.12391754244415544e-6) /
466+
@evalpoly(x,1.0,1.93297600031287800,0.660790440069106542,0.198322636197663277,0.0272447293513279631,0.00399501571688512611,0.000362510989191199243,0.0000182930089855534336,2.06800780072894204e-6)
467+
x < 20 && return exp(x) *
468+
@evalpoly(x,-1.77183291754640123,0.795659966861260409,-0.221223333413388642,0.0328877243243796815,-0.00331846947191676458,0.000180945604349930285,-5.97641401680304362e-6,2.42151808626299747e-11) /
469+
@evalpoly(x,1.0,-2.10926998628216150,0.933357955421497965,-0.245433884954174394,0.0356954809772243699,-0.00348034743685382360,0.000186615220328647350,-5.97232581353392099e-6)
470+
471+
x > 710 && return Inf # overflow
472+
x⁻¹ = inv(x)
473+
474+
# minimax rational approximant in x⁻¹ for x ∈ [20,200]
475+
x < 200 && return exp(x) *
476+
@evalpoly(x⁻¹, -5.29842699621003563e-14, +1.00000000004732488, -60.4361334939888359, +1327.83891720487710, -6810.63668974273961, -177755.383525765400,+3.00773484037048848e6, -1.53642380695372707e7, +2.08174653368702692e7) /
477+
@evalpoly(x⁻¹, 1.0, -61.4361334756161381, +1387.27504658395142, -8081.03888544858393, -172104.333927401741, +3.18903576285551101e6, -1.81873890267574206e7, +3.37312131843327704e7, -1.22198734384213631e7)
478+
479+
# asymptotic series eˣ/x ∑k!/xᵏ for x ≥ 200:
480+
return exp(x)*x⁻¹ * @evalpoly(x⁻¹, 1,1,2,6,24,120,720,5040)
481+
end
482+
483+
root = 0.37250741078136663446 # OEIS A091723
484+
dx = x - root
485+
if abs(dx) < 0.03
486+
# taylor series around real root of Ei
487+
return dx * @evalpoly(dx, 3.896215733907167310, -3.281607866398561671, 6.52237614543892570, -12.96969738353651704, 27.88629796294204998, -62.3788015289154187, 143.5349488096750988, -337.155827178746892, 804.531839982138251, -1943.79664572349884, 4743.76565040243084, -11673.46399116716364, 28926.9553054354509)
488+
else
489+
# TODO: crossover points and degrees could be tuned more
490+
return x 0.6 ? (x 0.053 ? (x 4.4e-3 ? @Ei_taylor64(x,4) :
491+
@Ei_taylor64(x,8)) :
492+
@Ei_taylor64(x,15)) :
493+
@Ei_taylor64(x,37)
494+
end
495+
end
496+
497+
expinti(z::Union{T,Rational{T}}) where {T<:Integer} = expinti(float(z))
498+
expinti(z::Float32) = Float32(expinti(Float64(z)))

0 commit comments

Comments
 (0)