@@ -48,7 +48,7 @@ macro E₁_cf64(x, n::Integer)
48
48
append! (num_expr. args, Float64 .(p. coeffs))
49
49
den_expr = :(@evalpoly $ xesc)
50
50
append! (den_expr. args, Float64 .(q. coeffs))
51
- :( exp ( - $ xesc) * $ num_expr / $ den_expr )
51
+ :( $ num_expr / $ den_expr )
52
52
end
53
53
54
54
function E₁_taylor_coefficients (:: Type{T} , n:: Integer ) where {T<: Number }
@@ -75,75 +75,88 @@ macro E₁_taylor64(z, n::Integer)
75
75
end
76
76
77
77
# adapted from Steven G Johnson's initial implementation: issue #19
78
- function expint_opt (x:: Float64 )
78
+ function expint_opt (x:: Float64 , :: Val{expscaled} = Val {false} ()) where {expscaled}
79
79
x < 0 && throw (DomainError (x, " negative argument, convert to complex first" ))
80
80
x == 0 && return Inf
81
+ # E_1(inf) * exp(inf) is indeterminate
82
+ x == Inf && return expscaled ? NaN : zero (x)
81
83
if x > 2.15
84
+ mult = expscaled ? 1 : exp (- x)
82
85
# minimax rational approximations to E_1(x)/exp(-x):
83
- x < 4.0 && return exp ( - x) *
86
+ x < 4.0 && return mult *
84
87
@evalpoly (x, 3.600530862438501481559423277418128014798 , 28.73031134165011013783185685393062481126 , 46.04314409968065653003548224846863877481 , 21.47189493062368074985000918414086604187 , 2.719957622921613321844755385973197500235 , 1.508750885580864752293599048121678438568e-6 ) /
85
88
@evalpoly (x, 1.0 , 18.06743589038646654075831055159865459831 , 61.19456872238615922515377354442679999566 , 64.81772518730116701207299231777089576859 , 24.19034591054828198408354214931112970741 , 2.720026796991567940556850921390829046015 )
86
- x < 10.0 && return exp ( - x) *
89
+ x < 10.0 && return mult *
87
90
@evalpoly (x, 3.149019890512432117647119992448352099575 , 14.77395058090815888166486507990452627136 , 14.78214309058953358717796744960600201013 , 4.559401130686434886620102186841739864936 , 0.4027858394909585103775445204576054721422 , 2.302781920509468929446800686773538387432e-9 ) /
88
91
@evalpoly (x, 1.0 , 11.65960373479520446458792926669115987821 , 26.20023773503894444535165299118172674849 , 18.93899893550582921168134979000319186841 , 4.962178168140565906794561070524079194193 , 0.4027860481050182948737116109665534358805 )
89
- x < 20.0 && return exp ( - x) *
92
+ x < 20.0 && return mult *
90
93
@evalpoly (x, 2.564801308922428705318296668924369454617 , 5.482252510134574167659359513298970499768 , 2.379528224853089764405551768869103662657 , 0.2523431276282591480166881146593592650031 , 1.444719769329975045925053905197199934930e-9 , - 8.977332061106791470409502623202677045468e-12 ) /
91
94
@evalpoly (x, 1.0 , 6.421214405318108272004472721910023284626 , 7.609584052290707052877352911548283916247 , 2.631866613851763364839413823973711355890 , 0.2523432345539146248733539983749722854603 )
92
- x < 200.0 && return @E₁_cf64 (x, 8 ) # fallback continued fraction
93
- return x < 740.0 ? @E₁_cf64 (x, 4 ) : 0.0 # underflow
95
+ x < 200.0 && return mult * @E₁_cf64 (x, 8 ) # fallback continued fraction
96
+ return mult * @E₁_cf64 (x, 4 )
94
97
else
95
98
# crossover point to taylor should be tuned more
96
- return x ≤ 0.6 ? (x ≤ 0.053 ? (x ≤ 4.4e-3 ? @E₁_taylor64 (x,4 ) :
99
+ mult = expscaled ? exp (x) : 1
100
+ return mult * (x ≤ 0.6 ? (x ≤ 0.053 ? (x ≤ 4.4e-3 ? @E₁_taylor64 (x,4 ) :
97
101
@E₁_taylor64 (x,8 )) :
98
102
@E₁_taylor64 (x,15 )) :
99
- @E₁_taylor64 (x,37 )
103
+ @E₁_taylor64 (x,37 ))
100
104
end
101
105
end
102
106
103
- function expint_opt (z:: Complex{Float64} )
107
+ function expint_opt (z:: Complex{Float64} , :: Val{expscaled} = Val {false} ()) where {expscaled}
104
108
x² = real (z)^ 2
105
109
y² = imag (z)^ 2
106
110
if x² + 0.233 * y² ≥ 7.84 # use cf expansion, ≤ 30 terms
107
- if (x² ≥ 546121 ) & (real (z) > 0 ) # underflow
108
- return zero (z)
109
- elseif x² + 0.401 * y² ≥ 58.0 # ≤ 15 terms
111
+ mult = expscaled ? 1 : exp (- z)
112
+ # if (x² ≥ 546121) & (real(z) > 0) # underflow
113
+ # return zero(z)
114
+ if x² + 0.401 * y² ≥ 58.0 # ≤ 15 terms
110
115
if x² + 0.649 * y² ≥ 540.0 # ≤ 8 terms
111
- x² + y² ≥ 4e4 && return @E₁_cf64 (z, 4 )
112
- return @E₁_cf64 (z, 8 )
116
+ x² + y² ≥ 4e4 && return mult * @E₁_cf64 (z, 4 )
117
+ return mult * @E₁_cf64 (z, 8 )
113
118
end
114
- return @E₁_cf64 (z, 15 )
119
+ return mult * @E₁_cf64 (z, 15 )
115
120
end
116
- return @E₁_cf64 (z, 30 )
121
+ return mult * @E₁_cf64 (z, 30 )
117
122
else # use Taylor expansion, ≤ 37 terms
118
123
r² = x² + y²
119
- return r² ≤ 0.36 ? (r² ≤ 2.8e-3 ? (r² ≤ 2e-7 ? @E₁_taylor64 (z,4 ) :
124
+ mult = expscaled ? exp (z) : 1
125
+ return mult * (r² ≤ 0.36 ? (r² ≤ 2.8e-3 ? (r² ≤ 2e-7 ? @E₁_taylor64 (z,4 ) :
120
126
@E₁_taylor64 (z,8 )) :
121
127
@E₁_taylor64 (z,15 )) :
122
- @E₁_taylor64 (z,37 )
128
+ @E₁_taylor64 (z,37 ))
123
129
end
124
130
end
125
131
126
- function expint (z:: Complex{Float64} )
132
+ function _expint (z:: Complex{Float64} , :: Val{expscaled} = Val {false} ()) where {expscaled}
127
133
if real (z) < 0
128
- return expint (1 , z)
134
+ return _expint (1 , z, 1000 , Val {expscaled} () )
129
135
else
130
- return expint_opt (z)
136
+ return expint_opt (z, Val {expscaled} () )
131
137
end
132
138
end
139
+ expint (z:: Complex{Float64} ) = _expint (z)
140
+ expintx (z:: Complex{Float64} ) = _expint (z, Val {true} ())
133
141
expint (x:: Float64 ) = expint_opt (x)
142
+ expintx (x:: Float64 ) = expint_opt (x, Val {true} ())
134
143
135
144
function expint (x:: BigFloat )
136
145
iszero (x) && return Inf
137
146
signbit (x) && throw (DomainError (x, " negative argument, convert to complex first" ))
138
147
return - expinti (- x)
139
148
end
140
149
141
- expint (z:: Union{T,Complex{T},Rational{T},Complex{Rational{T}}} ) where {T<: Integer } = expint (float (z))
142
150
expint (x:: Number ) = expint (1 , x)
143
- expint (z:: Float32 ) = Float32 (expint (Float64 (z)))
144
- expint (z:: ComplexF32 ) = ComplexF32 (expint (ComplexF64 (z)))
145
- expint (z:: Float16 ) = Float16 (expint (Float64 (z)))
146
- expint (z:: ComplexF16 ) = ComplexF16 (expint (ComplexF64 (z)))
151
+ expint (z:: Union{T,Complex{T},Rational{T},Complex{Rational{T}}} ) where {T<: Integer } = expint (float (z))
152
+ for f in (:expint , :expintx )
153
+ for T in (Float16, Float32)
154
+ @eval $ f (x:: $T ) = $ T ($ f (Float64 (x)))
155
+ end
156
+ for CT in (ComplexF16, ComplexF32)
157
+ @eval $ f (x:: $CT ) = $ CT ($ f (ComplexF64 (x)))
158
+ end
159
+ end
147
160
148
161
# Continued fraction for En(ν, z) that doesn't use a term with
149
162
# the gamma function: https://functions.wolfram.com/GammaBetaErf/ExpIntegralE/10/0001/
@@ -186,17 +199,7 @@ function En_cf_nogamma(ν::Number, z::Number, n::Int=1000)
186
199
end
187
200
end
188
201
189
- exppart = exp (- z)
190
- if isinf (exppart)
191
- if A isa Real
192
- return sign (A)* sign (B)* exp (- z + log (abs (A)) - log (abs (B))), iters
193
- else
194
- exp (- z + log (A) - log (B)), iters
195
- end
196
- else
197
- cfpart = A/ B
198
- return cfpart * exppart, iters
199
- end
202
+ return A/ B, iters
200
203
end
201
204
202
205
# Calculate Γ(1 - ν) * z^(ν-1) safely
@@ -243,14 +246,7 @@ function En_cf_gamma(ν::Number, z::Number, n::Int=1000)
243
246
end
244
247
245
248
gammapart = En_safe_gamma_term (ν, z)
246
- cfpart = exp (- z)
247
- if isinf (cfpart)
248
- factor = sign (real (cfpart)) + sign (imag (cfpart))* im
249
- cfpart = exp (- z + log (B) - log (A))
250
- else
251
- cfpart *= B/ A
252
- end
253
- return gammapart, - cfpart, iters
249
+ return gammapart, - B/ A, iters
254
250
end
255
251
256
252
# picks between continued fraction representations in
@@ -262,10 +258,10 @@ function En_cf(ν::Number, z::Number, niter::Int=1000)
262
258
gammaabs, cfabs = abs (gammapart), abs (cfpart)
263
259
if gammaabs != Inf && gammaabs > 1.0 && gammaabs > cfabs
264
260
# significant gamma part, use this
265
- return gammapart + cfpart, iters, true
261
+ return gammapart, cfpart, iters, true
266
262
end
267
263
end
268
- return En_cf_nogamma (ν, z, niter)... , false
264
+ return zero (z), En_cf_nogamma (ν, z, niter)... , false
269
265
end
270
266
271
267
# Compute expint(ν, z₀+Δ) given start = expint(ν, z₀), as described by [Amos 1980].
@@ -360,16 +356,16 @@ function En_imagbranchcut(ν::Number, z::Number)
360
356
return - 2 * lgammasign * e1 * π * e2 * exp ((ν- 1 )* log (complex (a)) - lgamma) * im
361
357
end
362
358
363
- """
364
- expint(z)
365
- expint(ν, z)
366
-
367
- Computes the exponential integral ``\\ operatorname{E}_\\ nu(z) = \\ int_0^\\ infty \\ frac{e^{-zt}}{t^\\ nu} dt``.
368
- If ``\\ nu`` is not specified, ``\\ nu=1`` is used. Arbitrary complex ``\\ nu`` and ``z`` are supported.
359
+ function En_safeexpmult (z, a)
360
+ zexp = exp (z)
361
+ if isinf (zexp) || iszero (zexp)
362
+ return a isa Real ? sign (a) * exp (z + log (abs (a))) : exp (z + log (a))
363
+ else
364
+ return zexp* a
365
+ end
366
+ end
369
367
370
- External links: [DLMF](https://dlmf.nist.gov/8.19), [Wikipedia](https://en.wikipedia.org/wiki/Exponential_integral)
371
- """
372
- function expint (ν:: Number , z:: Number , niter:: Int = 1000 )
368
+ function _expint (ν:: Number , z:: Number , niter:: Int = 1000 , :: Val{expscaled} = Val {false} ()) where {expscaled}
373
369
if abs (ν) > 50 && ! (isreal (ν) && real (ν) > 0 )
374
370
throw (ArgumentError (" Unsupported order |ν| > 50 off the positive real axis" ))
375
371
end
@@ -389,23 +385,32 @@ function expint(ν::Number, z::Number, niter::Int=1000)
389
385
end
390
386
391
387
if ν == 0
392
- return exp (- z) / z
388
+ return expscaled ? inv (z) : exp (- z)/ z
393
389
elseif ν == 1 && real (z) > 0 && z isa Union{Float64, Complex{Float64}}
394
- return expint_opt (z)
390
+ return expint_opt (z, Val {expscaled} () )
395
391
end
396
392
# asymptotic test for underflow when Re z → ∞
397
393
# https://functions.wolfram.com/GammaBetaErf/ExpIntegralE/06/02/0003/
398
- if real (z) > - log (nextfloat (zero (real (z))))+ 1 # exp(-z) is zero
394
+ if ! expscaled && real (z) > - log (nextfloat (zero (real (z))))+ 1 # exp(-z) is zero
399
395
return zero (z)
400
396
end
401
397
402
398
if abs2 (z) < 9
403
399
# use Taylor series about the origin for small z
404
- return En_expand_origin (ν, z)
400
+ mult = expscaled ? exp (z) : 1
401
+ return mult * En_expand_origin (ν, z)
405
402
end
406
403
407
404
if z isa Real || real (z) > 0
408
- return first (real (z) > 0 ? En_cf (ν, z, niter) : En_cf_nogamma (ν, z, niter))
405
+ gmult = expscaled ? exp (z) : 1
406
+ if real (z) > 0
407
+ g, cf, _ = En_cf (ν, z, niter)
408
+ else
409
+ g, cf, _ = zero (z), En_cf_nogamma (ν, z, niter)...
410
+ end
411
+ g != 0 && (g *= gmult)
412
+ cf = expscaled ? cf : En_safeexpmult (- z, cf)
413
+ return g + cf
409
414
else
410
415
# Procedure for z near the negative real axis based on
411
416
# (without looking at the accompanying source code):
@@ -421,7 +426,8 @@ function expint(ν::Number, z::Number, niter::Int=1000)
421
426
# start with small imaginary part if exactly on negative real axis
422
427
imstart = (imz == 0 ) ? abs (z)* sqrt (eps (typeof (real (z)))) : imz
423
428
z₀ = rez + imstart* im
424
- E_start, i, _ = En_cf (ν, z₀, quick_niter)
429
+ g_start, cf_start, i, _ = En_cf (ν, z₀, quick_niter)
430
+ E_start = g_start + En_safeexpmult (- z₀, cf_start)
425
431
isnan (E_start) && return E_start
426
432
if imz > 0 && i < nmax
427
433
# didn't need to take any steps
@@ -431,7 +437,8 @@ function expint(ν::Number, z::Number, niter::Int=1000)
431
437
# double imaginary part until in region with fast convergence
432
438
imstart *= 2
433
439
z₀ = rez + imstart* im
434
- E_start, i, _ = En_cf (ν, z₀, quick_niter)
440
+ g_start, cf_start, i, _ = En_cf (ν, z₀, quick_niter)
441
+ E_start = g_start + En_safeexpmult (- z₀, cf_start)
435
442
end
436
443
437
444
# nsteps chosen so |Δ| ≤ 0.5
@@ -453,17 +460,38 @@ function expint(ν::Number, z::Number, niter::Int=1000)
453
460
sign = bit ? 1 : - 1
454
461
if isreal (ν)
455
462
# can separate real/im in case of real ν
456
- return real (En) - sign * imag (bc)/ 2 * im
463
+ En = real (En) - sign * imag (bc)/ 2 * im
457
464
else
458
- return bit ? En : En + bc
465
+ En = bit ? En : En + bc
459
466
end
460
- else
461
- return En
462
467
end
468
+ return expscaled ? exp (z) * En : En
463
469
end
464
- throw (" unreachable" )
465
470
end
466
471
472
+ """
473
+ expint(z)
474
+ expint(ν, z)
475
+
476
+ Computes the exponential integral ``\\ operatorname{E}_\\ nu(z) = \\ int_0^\\ infty \\ frac{e^{-zt}}{t^\\ nu} dt``.
477
+ If ``\\ nu`` is not specified, ``\\ nu=1`` is used. Arbitrary complex ``\\ nu`` and ``z`` are supported.
478
+
479
+ External links: [DLMF](https://dlmf.nist.gov/8.19), [Wikipedia](https://en.wikipedia.org/wiki/Exponential_integral)
480
+ """
481
+ expint (ν:: Number , z:: Number , niter:: Int = 1000 ) = _expint (ν, z, niter, Val {false} ())
482
+
483
+
484
+ """
485
+ expintx(z)
486
+ expintx(ν, z)
487
+
488
+ Computes the scaled exponential integral ``\\ exp(z) \\ operatorname{E}_\\ nu(z) = \\ exp(z) \\ int_0^\\ infty \\ frac{e^{-zt}}{t^\\ nu} dt``.
489
+ If ``\\ nu`` is not specified, ``\\ nu=1`` is used. Arbitrary complex ``\\ nu`` and ``z`` are supported.
490
+
491
+ See also: [`expint(ν, z)`](@ref SpecialFunctions.expint)
492
+ """
493
+ expintx (ν:: Number , z:: Number , niter:: Int = 1000 ) = _expint (ν, z, niter, Val {true} ())
494
+
467
495
# #############################################################################
468
496
# expinti function Ei
469
497
0 commit comments