@@ -140,8 +140,10 @@ function besseli(nu, x::T) where T <: Union{Float32, Float64}
140
140
nu == 0 && return besseli0 (x)
141
141
nu == 1 && return besseli1 (x)
142
142
143
- if x > maximum ((T (50 ), nu^ 2 / 2 ))
143
+ if x > maximum ((T (30 ), nu^ 2 / 4 ))
144
144
return T (besseli_large_argument (nu, x))
145
+ elseif x <= 2 * sqrt (nu + 1 )
146
+ return T (besseli_small_arguments (nu, x))
145
147
elseif nu < 100
146
148
return T (_besseli_continued_fractions (nu, x))
147
149
else
@@ -162,8 +164,8 @@ function besselix(nu, x::T) where T <: Union{Float32, Float64}
162
164
branch = 60
163
165
if nu < branch
164
166
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)
167
169
else
168
170
return besseli_large_orders_scaled (nu, x)
169
171
end
@@ -197,6 +199,7 @@ function _besseli_continued_fractions(nu, x::T) where T
197
199
S = promote_type (T, Float64)
198
200
xx = S (x)
199
201
knu, knum1 = up_recurrence (xx, besselk0 (xx), besselk1 (xx), nu)
202
+ (iszero (knu) || iszero (knum1)) && return throw (DomainError (x, " Overflow error" ))
200
203
return 1 / (x * (knum1 + knu / steed (nu, x)))
201
204
end
202
205
@@ -220,20 +223,54 @@ end
220
223
221
224
function besseli_large_argument (v, z:: T ) where T
222
225
MaxIter = 1000
223
- S = promote_type (T, Float64)
224
- z = S (z)
225
- coef = exp (z) / sqrt (2 * S (pi )* z)
226
- fv2 = 4 v^ 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)
228
230
res = term
229
231
s = - term
230
232
for i in 1 : MaxIter
233
+ i = T (i)
231
234
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)
233
236
res = muladd (term, s, res)
234
237
s = - s
235
238
abs (term) <= eps (T) && break
236
239
end
237
- return res* coef
240
+ return res * coef * a
238
241
end
239
242
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