Skip to content

Commit 3b94ce4

Browse files
committed
add power series
1 parent d982cf3 commit 3b94ce4

File tree

1 file changed

+47
-10
lines changed

1 file changed

+47
-10
lines changed

src/besseli.jl

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,10 @@ function besseli(nu, x::T) where T <: Union{Float32, Float64}
140140
nu == 0 && return besseli0(x)
141141
nu == 1 && return besseli1(x)
142142

143-
if x > maximum((T(50), nu^2 / 2))
143+
if x > maximum((T(30), nu^2 / 4))
144144
return T(besseli_large_argument(nu, x))
145+
elseif x <= 2 * sqrt(nu + 1)
146+
return T(besseli_small_arguments(nu, x))
145147
elseif nu < 100
146148
return T(_besseli_continued_fractions(nu, x))
147149
else
@@ -162,8 +164,8 @@ function besselix(nu, x::T) where T <: Union{Float32, Float64}
162164
branch = 60
163165
if nu < branch
164166
inp1 = besseli_large_orders_scaled(branch + 1, x)
165-
in = besseli_large_orders_scaled(branch, x)
166-
return down_recurrence(x, in, inp1, nu, branch)
167+
inu = besseli_large_orders_scaled(branch, x)
168+
return down_recurrence(x, inu, inp1, nu, branch)
167169
else
168170
return besseli_large_orders_scaled(nu, x)
169171
end
@@ -197,6 +199,7 @@ function _besseli_continued_fractions(nu, x::T) where T
197199
S = promote_type(T, Float64)
198200
xx = S(x)
199201
knu, knum1 = up_recurrence(xx, besselk0(xx), besselk1(xx), nu)
202+
(iszero(knu) || iszero(knum1)) && return throw(DomainError(x, "Overflow error"))
200203
return 1 / (x * (knum1 + knu / steed(nu, x)))
201204
end
202205

@@ -220,20 +223,54 @@ end
220223

221224
function besseli_large_argument(v, z::T) where T
222225
MaxIter = 1000
223-
S = promote_type(T, Float64)
224-
z = S(z)
225-
coef = exp(z) / sqrt(2*S(pi)*z)
226-
fv2 = 4v^2
227-
term = one(S)
226+
a = exp(z / 2)
227+
coef = a / sqrt(2 * T(pi) * z)
228+
fv2 = 4 * v^2
229+
term = one(T)
228230
res = term
229231
s = -term
230232
for i in 1:MaxIter
233+
i = T(i)
231234
offset = muladd(2, i, -1)
232-
term *= S(0.125)*(muladd(offset, -offset, fv2)) / (z*i)
235+
term *= T(0.125) * muladd(offset, -offset, fv2) / (z * i)
233236
res = muladd(term, s, res)
234237
s = -s
235238
abs(term) <= eps(T) && break
236239
end
237-
return res*coef
240+
return res * coef * a
238241
end
239242

243+
# power series definition of besseli
244+
# fast convergence for small arguments
245+
# for large orders and small arguments there is increased roundoff error ~ 1e-14
246+
# need to investigate further if this can be mitigated
247+
function besseli_small_arguments(v, z::T) where T
248+
if v < 20
249+
coef = (z / 2)^v / factorial(v)
250+
else
251+
coef = v*log(z / 2)
252+
coef -= _loggam(v + 1)
253+
coef = exp(coef)
254+
end
255+
256+
MaxIter = 1000
257+
out = one(T)
258+
zz = z^2 / 4
259+
a = one(T)
260+
for k in 0:MaxIter
261+
a *= zz / (k + 1) / (k + v + 1)
262+
out += a
263+
a <= eps(T) && break
264+
end
265+
return coef * out
266+
end
267+
@inline function _loggam(x)
268+
xinv = inv(x)
269+
xinv2 = xinv * xinv
270+
out = (x - 0.5) * log(x) - x + 9.1893853320467274178032927e-01
271+
out += xinv * evalpoly(
272+
xinv2, (8.3333333333333333333333368e-02, -2.7777777777777777777777776e-03,
273+
7.9365079365079365079365075e-04, -5.9523809523809523809523806e-04)
274+
)
275+
return out
276+
end

0 commit comments

Comments
 (0)