Skip to content

Commit 081286a

Browse files
committed
add Float32 tests
1 parent b0390d5 commit 081286a

File tree

2 files changed

+61
-21
lines changed

2 files changed

+61
-21
lines changed

src/airy.jl

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ end
216216
airyai_power_series_cutoff(x::T, y::T) where T <: Float64 = x < 2 && abs(y) > -1.4*(x + 5.5)
217217
airyai_power_series_cutoff(x::T, y::T) where T <: Float32 = x < 4.5f0 && abs(y) > -1.4f0*(x + 9.5f0)
218218

219-
function airyai_power_series(x::ComplexOrReal{T}) where T
219+
function airyai_power_series(x::ComplexOrReal{T}; tol=eps(T)) where T
220220
S = eltype(x)
221221
iszero(x) && return S(0.3550280538878172)
222222
MaxIter = 3000
@@ -230,18 +230,20 @@ function airyai_power_series(x::ComplexOrReal{T}) where T
230230
for i in 0:MaxIter
231231
ai1 += t
232232
ai2 += t2
233-
abs(t) < eps(T) * abs(ai1) && break
233+
abs(t) < tol * abs(ai1) && break
234234
t *= x3 * inv(9*(i + one(T))*(i + T(2)/3))
235235
t2 *= x3 * inv(9*(i + one(T))*(i + T(4)/3))
236236
end
237237
return (ai1*3^(-T(2)/3) - ai2*3^(-T(4)/3))
238238
end
239+
airyai_power_series(x::Float32) = Float32(airyai_power_series(Float64(x), tol=eps(Float32)))
240+
airyai_power_series(x::ComplexF32) = ComplexF32(airyai_power_series(ComplexF64(x), tol=eps(Float32)))
239241

240242
#####
241243
##### Power series for airyaiprime(x)
242244
#####
243245

244-
function airyaiprime_power_series(x::ComplexOrReal{T}) where T
246+
function airyaiprime_power_series(x::ComplexOrReal{T}; tol=eps(T)) where T
245247
S = eltype(x)
246248
iszero(x) && return S(-0.2588194037928068)
247249
MaxIter = 3000
@@ -255,25 +257,27 @@ function airyaiprime_power_series(x::ComplexOrReal{T}) where T
255257
for i in 0:MaxIter
256258
ai1 += t
257259
ai2 += t2
258-
abs(t) < eps(T) * abs(ai1) && break
260+
abs(t) < tol * abs(ai1) && break
259261
t *= x3 * inv(9*(i + one(T))*(i + T(1)/3))
260262
t2 *= x3 * inv(9*(i + one(T))*(i + T(5)/3))
261263
end
262264
return -ai1*3^(-T(1)/3) + ai2*3^(-T(5)/3)
263265
end
266+
airyaiprime_power_series(x::Float32) = Float32(airyaiprime_power_series(Float64(x), tol=eps(Float32)))
267+
airyaiprime_power_series(x::ComplexF32) = ComplexF32(airyaiprime_power_series(ComplexF64(x), tol=eps(Float32)))
264268

265269
#####
266270
##### Power series for airybi(x)
267271
#####
268272

269273
# cutoffs for power series valid for both airybi and airybiprime
270274
# has a more complicated validity as it works well close to positive real line and for small negative arguments also works for angle(z) ~ 2pi/3
271-
# the statements are somewhat complicated but we absolutely want to hit this branch when we can as the other algorithms are 10x slower
275+
# the statements are somewhat complicated but we want to hit this branch when we can as the other algorithms are 10x slower
272276
# the Float32 cutoff can be simplified because it overlaps with the large argument expansion so there isn't a need for more complicated statements
273277
airybi_power_series_cutoff(x::T, y::T) where T <: Float64 = (abs(y) > -1.4*(x + 5.5) && abs(y) < -2.2*(x - 4)) || (x > zero(T) && abs(y) < 3)
274278
airybi_power_series_cutoff(x::T, y::T) where T <: Float32 = abs(complex(x, y)) < 9
275279

276-
function airybi_power_series(x::ComplexOrReal{T}) where T
280+
function airybi_power_series(x::ComplexOrReal{T}; tol=eps(T)) where T
277281
S = eltype(x)
278282
iszero(x) && return S(0.6149266274460007)
279283
MaxIter = 3000
@@ -287,18 +291,20 @@ function airybi_power_series(x::ComplexOrReal{T}) where T
287291
for i in 0:MaxIter
288292
ai1 += t
289293
ai2 += t2
290-
abs(t) < eps(T) * abs(ai1) && break
294+
abs(t) < tol * abs(ai1) && break
291295
t *= x3 * inv(9*(i + one(T))*(i + T(2)/3))
292296
t2 *= x3 * inv(9*(i + one(T))*(i + T(4)/3))
293297
end
294298
return (ai1*3^(-T(1)/6) + ai2*3^(-T(5)/6))
295299
end
300+
airybi_power_series(x::Float32) = Float32(airybi_power_series(Float64(x), tol=eps(Float32)))
301+
airybi_power_series(x::ComplexF32) = ComplexF32(airybi_power_series(ComplexF64(x), tol=eps(Float32)))
296302

297303
#####
298304
##### Power series for airybiprime(x)
299305
#####
300306

301-
function airybiprime_power_series(x::ComplexOrReal{T}) where T
307+
function airybiprime_power_series(x::ComplexOrReal{T}; tol=eps(T)) where T
302308
S = eltype(x)
303309
iszero(x) && return S(0.4482883573538264)
304310
MaxIter = 3000
@@ -312,12 +318,14 @@ function airybiprime_power_series(x::ComplexOrReal{T}) where T
312318
for i in 0:MaxIter
313319
ai1 += t
314320
ai2 += t2
315-
abs(t) < eps(T) * abs(ai1) && break
321+
abs(t) < tol * abs(ai1) && break
316322
t *= x3 * inv(9*(i + one(T))*(i + T(1)/3))
317323
t2 *= x3 * inv(9*(i + one(T))*(i + T(5)/3))
318324
end
319325
return (ai1*3^(T(1)/6) + ai2*3^(-T(7)/6))
320326
end
327+
airybiprime_power_series(x::Float32) = Float32(airybiprime_power_series(Float64(x), tol=eps(Float32)))
328+
airybiprime_power_series(x::ComplexF32) = ComplexF32(airybiprime_power_series(ComplexF64(x), tol=eps(Float32)))
321329

322330
#####
323331
##### Large argument expansion for airy functions
@@ -422,7 +430,7 @@ function airybiprime_large_argument(z::Complex{T}) where T
422430
end
423431

424432
# see equations 24 and relations using eq 25 and 26 in [1]
425-
function airy_large_arg_a(x::ComplexOrReal{T}) where T
433+
function airy_large_arg_a(x::ComplexOrReal{T}; tol=eps(T)*40) where T
426434
S = eltype(x)
427435
MaxIter = 3000
428436
xsqr = sqrt(x)
@@ -432,13 +440,13 @@ function airy_large_arg_a(x::ComplexOrReal{T}) where T
432440
a = 4*xsqr*x
433441
for i in 0:MaxIter
434442
out += t
435-
abs(t) < eps(T)*50 * abs(out) && break
443+
abs(t) < tol*abs(out) && break
436444
t *= -3*(i + one(T)/6) * (i + T(5)/6) / (a*(i + one(T)))
437445
end
438-
return out * exp(-a / 6) / (pi^(3/2) * sqrt(xsqr))
446+
return out * exp(-a / 6) / (π^(T(3)/2) * sqrt(xsqr))
439447
end
440448

441-
function airy_large_arg_b(x::ComplexOrReal{T}) where T
449+
function airy_large_arg_b(x::ComplexOrReal{T}; tol=eps(T)*40) where T
442450
S = eltype(x)
443451
MaxIter = 3000
444452
xsqr = sqrt(x)
@@ -448,13 +456,13 @@ function airy_large_arg_b(x::ComplexOrReal{T}) where T
448456
a = 4*xsqr*x
449457
for i in 0:MaxIter
450458
out += t
451-
abs(t) < eps(T)*50 * abs(out) && break
459+
abs(t) < tol*abs(out) && break
452460
t *= 3*(i + one(T)/6) * (i + T(5)/6) / (a*(i + one(T)))
453461
end
454-
return out * exp(a / 6) / (pi^(3/2) * sqrt(xsqr))
462+
return out * exp(a / 6) / (π^(T(3)/2) * sqrt(xsqr))
455463
end
456464

457-
function airy_large_arg_c(x::ComplexOrReal{T}) where T
465+
function airy_large_arg_c(x::ComplexOrReal{T}; tol=eps(T)*40) where T
458466
S = eltype(x)
459467
MaxIter = 3000
460468
xsqr = sqrt(x)
@@ -466,13 +474,13 @@ function airy_large_arg_c(x::ComplexOrReal{T}) where T
466474
a = 4*xsqr*x
467475
for i in 0:MaxIter
468476
out += t
469-
abs(t) < eps(T)*50* abs(out) && break
477+
abs(t) < tol*abs(out) && break
470478
t *= -3*(i - one(T)/6) * (i + T(7)/6) / (a*(i + one(T)))
471479
end
472-
return out * exp(-a / 6) * sqrt(xsqr) / pi^(3/2)
480+
return out * exp(-a / 6) * sqrt(xsqr) / π^(T(3)/2)
473481
end
474482

475-
function airy_large_arg_d(x::ComplexOrReal{T}) where T
483+
function airy_large_arg_d(x::ComplexOrReal{T}; tol=eps(T)*40) where T
476484
S = eltype(x)
477485
MaxIter = 3000
478486
xsqr = sqrt(x)
@@ -484,12 +492,25 @@ function airy_large_arg_d(x::ComplexOrReal{T}) where T
484492
a = 4*xsqr*x
485493
for i in 0:MaxIter
486494
out += t
487-
abs(t) < eps(T)*50 * abs(out) && break
495+
abs(t) < tol*abs(out) && break
488496
t *= 3*(i - one(T)/6) * (i + T(7)/6) / (a*(i + one(T)))
489497
end
490-
return -out * exp(a / 6) * sqrt(xsqr) / pi^(3/2)
498+
return -out * exp(a / 6) * sqrt(xsqr) / π^(T(3)/2)
491499
end
492500

501+
# negative arguments of airy functions oscillate around zero so as x -> -Inf it is more prone to cancellation
502+
# to give best accuracy it is best to promote to Float64 numbers until the Float32 tolerance
503+
airy_large_arg_a(x::Float32) = (airy_large_arg_a(Float64(x), tol=eps(Float32)))
504+
airy_large_arg_a(x::ComplexF32) = (airy_large_arg_a(ComplexF64(x), tol=eps(Float32)))
505+
506+
airy_large_arg_b(x::Float32) = Float32(airy_large_arg_b(Float64(x), tol=eps(Float32)))
507+
airy_large_arg_b(x::ComplexF32) = ComplexF32(airy_large_arg_b(ComplexF64(x), tol=eps(Float32)))
508+
509+
airy_large_arg_c(x::Float32) = Float32(airy_large_arg_c(Float64(x), tol=eps(Float32)))
510+
airy_large_arg_c(x::ComplexF32) = ComplexF32(airy_large_arg_c(ComplexF64(x), tol=eps(Float32)))
511+
512+
airy_large_arg_d(x::Float32) = Float32(airy_large_arg_d(Float64(x), tol=eps(Float32)))
513+
airy_large_arg_d(x::ComplexF32) = ComplexF32(airy_large_arg_d(ComplexF64(x), tol=eps(Float32)))
493514
# calculates besselk from the power series of besseli using the continued fraction and wronskian
494515
# this shift the order higher first to avoid cancellation in the power series of besseli along the imaginary axis
495516
# for real arguments this is not needed because besseli can be computed stably over the entire real axis

test/airy_test.jl

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,21 @@ for x in [0.0; 1e-17:0.1:100.0]
1616
@test isapprox(airybiprime(-x), SpecialFunctions.airybiprime(-x), rtol=5e-12)
1717
end
1818

19+
# Float32
20+
for x in [0.0; 0.5:0.5:30.0]
21+
@test isapprox(airyai(x), SpecialFunctions.airyai(x), rtol=2e-13)
22+
@test isapprox(airyai(-x), SpecialFunctions.airyai(-x), rtol=3e-12)
23+
24+
@test isapprox(airyaiprime(x), SpecialFunctions.airyaiprime(x), rtol=2e-13)
25+
@test isapprox(airyaiprime(-x), SpecialFunctions.airyaiprime(-x), rtol=5e-12)
26+
27+
@test isapprox(airybi(x), SpecialFunctions.airybi(x), rtol=2e-13)
28+
@test isapprox(airybi(-x), SpecialFunctions.airybi(-x), rtol=5e-12)
29+
30+
@test isapprox(airybiprime(x), SpecialFunctions.airybiprime(x), rtol=2e-13)
31+
@test isapprox(airybiprime(-x), SpecialFunctions.airybiprime(-x), rtol=5e-12)
32+
end
33+
1934
for x in [0.0, 0.01, 0.5, 1.0, 2.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 50.0], a in 0:pi/12:2pi
2035
z = x*exp(im*a)
2136
@test isapprox(airyai(z), SpecialFunctions.airyai(z), rtol=5e-10)
@@ -43,6 +58,10 @@ end
4358

4459
# test Float16 types
4560
@test airyai(Float16(1.2)) isa Float16
61+
@test airyai(ComplexF16(1.2)) isa ComplexF16
4662
@test airyaiprime(Float16(1.9)) isa Float16
63+
@test airyaiprime(ComplexF16(1.2)) isa ComplexF16
4764
@test airybi(Float16(1.2)) isa Float16
65+
@test airybi(ComplexF16(1.2)) isa ComplexF16
4866
@test airybiprime(Float16(1.9)) isa Float16
67+
@test airybiprime(ComplexF16(1.2)) isa ComplexF16

0 commit comments

Comments
 (0)