Skip to content

Commit 2d5c48e

Browse files
authored
expint: Type stability, NaNs (#248)
* Type stability, NaNs * eps() of type * NaN tests
1 parent 1549f98 commit 2d5c48e

File tree

2 files changed

+37
-19
lines changed

2 files changed

+37
-19
lines changed

src/expint.jl

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ const denom_10_20 = (1.0,
114114
0.2523432345539146248733539983749722854603)
115115

116116
# adapted from Steven G Johnson's initial implementation: issue #19
117-
function expint(x::Float64)
117+
function expint_opt(x::Float64)
118118
x < 0 && throw(DomainError(x, "negative argument, convert to complex first"))
119119
x == 0 && return Inf
120120
if x > 2.15
@@ -132,10 +132,7 @@ function expint(x::Float64)
132132
end
133133
end
134134

135-
function expint(z::Complex{Float64})
136-
if real(z) < 0
137-
return expint(1, z)
138-
end
135+
function expint_opt(z::Complex{Float64})
139136
= real(z)^2
140137
= imag(z)^2
141138
if+ 0.233* 7.84 # use cf expansion, ≤ 30 terms
@@ -158,6 +155,15 @@ function expint(z::Complex{Float64})
158155
end
159156
end
160157

158+
function expint(z::Complex{Float64})
159+
if real(z) < 0
160+
return expint(1, z)
161+
else
162+
return expint_opt(z)
163+
end
164+
end
165+
expint(x::Float64) = expint_opt(x)
166+
161167
expint(z::Union{T,Complex{T},Rational{T},Complex{Rational{T}}}) where {T<:Integer} = expint(float(z))
162168
expint(x::Number) = expint(1, x)
163169
expint(z::Float32) = Float32(expint(Float64(z)))
@@ -214,7 +220,7 @@ function En_cf_nogamma(ν::Number, z::Number, n::Int=1000)
214220
end
215221

216222
# Calculate Γ(1 - ν) * z^(ν-1) safely
217-
En_safe_gamma_term::Number, z::Number) = exp((ν - 1)*log(z) + loggamma(1 - ν))
223+
En_safe_gamma_term::Number, z::Number) = exp((ν - 1)*log(z) + loggamma(1 - oftype(z, ν)))
218224

219225
# continued fraction for En(ν, z) that uses the gamma function:
220226
# https://functions.wolfram.com/GammaBetaErf/ExpIntegralE/10/0005/
@@ -380,22 +386,24 @@ function expint(ν::Number, z::Number, niter::Int=1000)
380386
if abs(ν) > 50 && !(isreal(ν) && real(ν) > 0)
381387
throw(ArgumentError("Unsupported order |ν| > 50 off the positive real axis"))
382388
end
383-
ν, z = promote(ν, float(z))
384-
if typeof(z) <: Real && z < 0
389+
390+
z, = promote(float(z), ν)
391+
if isnan(ν) || isnan(z)
392+
return oftype(z, NaN) * z
393+
end
394+
395+
if z isa Real && (isinteger(ν) ? (z < 0 && ν > 0) : (z < 0 || ν < 0))
385396
throw(DomainError(z, "En will only return a complex result if called with a complex argument"))
386397
end
387398

388-
if z == 0.0
389-
if real(ν) > 0
390-
return 1 /- 1)
391-
else
392-
return oftype(z, Inf)
393-
end
399+
if z == 0
400+
return oftype(z, real(ν) > 0 ? 1/-1) : Inf)
394401
end
402+
395403
if ν == 0
396404
return exp(-z) / z
397405
elseif ν == 1 && real(z) > 0 && z isa Union{Float64, Complex{Float64}}
398-
return E₁(z)
406+
return expint_opt(z)
399407
end
400408
# asymptotic test for |z| → ∞
401409
# https://functions.wolfram.com/GammaBetaErf/ExpIntegralE/06/02/0003/
@@ -408,9 +416,8 @@ function expint(ν::Number, z::Number, niter::Int=1000)
408416
return En_expand_origin(ν, z)
409417
end
410418

411-
if real(z) > 0 && real(ν) > 0
412-
res, i = En_cf_nogamma(ν, z, niter)
413-
return res
419+
if z isa Real
420+
return first(z > 0 ? En_cf(ν, z, niter) : En_cf_nogamma(ν, z, niter))
414421
else
415422
# Procedure for z near the negative real axis based on
416423
# (without looking at the accompanying source code):
@@ -424,7 +431,7 @@ function expint(ν::Number, z::Number, niter::Int=1000)
424431

425432
quick_niter, nmax = 50, 45
426433
# start with small imaginary part if exactly on negative real axis
427-
imstart = (imz == 0) ? abs(z)*1e-8 : imz
434+
imstart = (imz == 0) ? abs(z)*sqrt(eps(typeof(real(z)))) : imz
428435
z₀ = rez + imstart*im
429436
E_start, i, _ = En_cf(ν, z₀, quick_niter)
430437
if imz > 0 && i < nmax

test/expint.jl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,17 @@ using Base.MathConstants
6363
@test expint(100000, 10+10im) -3.8087890502240534e-10 + 2.470010601657554e-10im
6464
@test expint(1000000, 10+10im) -3.8093198659567175e-11 + 2.4698678867697097e-11im
6565

66+
@test expint(-2, 3.3) 0.0200031425174657791728529428002375552480803611197081245457722070
67+
@test expint(-2, 3.3) isa Real
68+
@test expint(-2, -3.3) -4.745485121488663825992363318400378114254141655964565963
69+
@test expint(-2, -3.3) isa Real
70+
71+
@test isnan(expint(NaN))
72+
@test isnan(expint(NaN+NaN*im))
73+
@test isnan(expint(NaN, 1.0))
74+
@test isnan(expint(NaN, NaN))
75+
@test isnan(expint(NaN+NaN*im, NaN+NaN*im))
76+
6677
# |ν| > 50 not currently supported
6778
@test_throws ArgumentError expint(-100, 5) 2.3661006604908971561634280511260954613666084788690790e87
6879
@test_throws ArgumentError expint(-50+100im, 5+5im) 0.00004495913300988524775299480665674170202100859018442234 - 0.00003791061255142431660134050245626121520797056806394134im

0 commit comments

Comments
 (0)