Skip to content

Commit c99a050

Browse files
authored
Merge pull request #65 from JuliaMath/oscardssmith-Float32-gamma
add Float32 gamma
2 parents d9f15ef + fd136dc commit c99a050

File tree

2 files changed

+72
-18
lines changed

2 files changed

+72
-18
lines changed

src/gamma.jl

Lines changed: 59 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,31 @@
1-
# Adapted from Cephes Mathematical Library (MIT license https://en.smath.com/view/CephesMathLibrary/license) by Stephen L. Moshier
2-
gamma(x::Float64) = _gamma(x)
3-
gamma(x::Float32) = Float32(_gamma(Float64(x)))
4-
5-
function _gamma(x::Float64)
1+
# Float64 version adapted from Cephes Mathematical Library (MIT license https://en.smath.com/view/CephesMathLibrary/license) by Stephen L. Moshier
2+
function gamma(_x::Float64)
63
T = Float64
4+
x = _x
75
if x < 0
8-
(isinteger(x) || x==-Inf) && throw(DomainError(x, "NaN result for non-NaN input."))
9-
xp1 = abs(x) + 1.0
10-
return π / (sinpi(xp1) * _gamma(xp1))
6+
s = sinpi(_x)
7+
s == 0 && throw(DomainError(x, "NaN result for non-NaN input."))
8+
x = -x # Use this rather than the traditional x = 1-x to avoid roundoff.
9+
s *= x
1110
end
1211
isfinite(x) || return x
1312
if x > 11.5
1413
w = inv(x)
15-
s = (
14+
coefs = (1.0,
1615
8.333333333333331800504e-2, 3.472222222230075327854e-3, -2.681327161876304418288e-3, -2.294719747873185405699e-4,
1716
7.840334842744753003862e-4, 6.989332260623193171870e-5, -5.950237554056330156018e-4, -2.363848809501759061727e-5,
1817
7.147391378143610789273e-4
1918
)
20-
w = muladd(w, evalpoly(w, s), 1.0)
19+
w = evalpoly(w, coefs)
2120
# avoid overflow
2221
v = x ^ muladd(0.5, x, -0.25)
23-
y = v * (v / exp(x))
22+
res = SQ2PI(T) * v * (v / exp(x)) * w
2423

25-
return SQ2PI(T) * y * w
24+
if _x < 0
25+
return π / (res * s)
26+
else
27+
return res
28+
end
2629
end
2730
P = (
2831
1.000000000000000000009e0, 8.378004301573126728826e-1, 3.629515436640239168939e-1, 1.113062816019361559013e-1,
@@ -47,7 +50,50 @@ function _gamma(x::Float64)
4750
x -= 2.0
4851
p = evalpoly(x, P)
4952
q = evalpoly(x, Q)
50-
return z * p / q
53+
return _x < 0 ? π * q / (s * z * p) : z * p / q
54+
end
55+
56+
57+
function gamma(_x::Float32)
58+
x = Float64(_x)
59+
if _x < 0
60+
s = sinpi(x)
61+
s == 0 && throw(DomainError(_x, "NaN result for non-NaN input."))
62+
x = 1 - x
63+
end
64+
if x < 5
65+
z = 1.0
66+
while x > 1
67+
x -= 1
68+
z *= x
69+
end
70+
num = evalpoly(x, (1.0, 0.41702538904450015, 0.24081703455575904, 0.04071509011391178, 0.015839573267537377))
71+
den = x*evalpoly(x, (1.0, 0.9942411061082665, -0.17434932941689474, -0.13577921102050783, 0.03028452206514555))
72+
res = z * num / den
73+
else
74+
x -= 1
75+
w = evalpoly(inv(x), (2.506628299028453, 0.20888413086840676, 0.008736513049552962, -0.007022997182153692, 0.0006787969600290756))
76+
res = @fastmath sqrt(x) * exp(log(x*1/ℯ) * x) * w
77+
end
78+
return Float32(_x < 0 ? π / (s * res) : res)
79+
end
80+
81+
function gamma(_x::Float16)
82+
x = Float32(_x)
83+
if _x < 0
84+
s = sinpi(x)
85+
s == 0 && throw(DomainError(_x, "NaN result for non-NaN input."))
86+
x = 1 - x
87+
end
88+
x > 14 && return Float16(ifelse(_x > 0, Inf32, 0f0))
89+
z = 1f0
90+
while x > 1
91+
x -= 1
92+
z *= x
93+
end
94+
num = evalpoly(x, (1.0f0, 0.4170254f0, 0.24081704f0, 0.04071509f0, 0.015839573f0))
95+
den = x*evalpoly(x, (1.0f0, 0.9942411f0, -0.17434932f0, -0.13577922f0, 0.030284522f0))
96+
return Float16(_x < 0 ? Float32(π)*den / (s*z*num) : z * num / den)
5197
end
5298

5399
function gamma(n::Integer)

test/gamma_test.jl

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
1-
x = rand(10000)*170
2-
@test SpecialFunctions.gamma.(BigFloat.(x)) Bessels.gamma.(x)
3-
@test SpecialFunctions.gamma.(BigFloat.(-x)) Bessels.gamma.(-x)
4-
@test isnan(Bessels.gamma(NaN))
5-
@test isinf(Bessels.gamma(Inf))
1+
for (T, max, rtol) in ((Float16, 13, 1.0), (Float32, 43, 1.0), (Float64, 170, 7))
2+
v = rand(T, 10000)*max
3+
for x in v
4+
@test isapprox(T(SpecialFunctions.gamma(widen(x))), Bessels.gamma(x), rtol=rtol*eps(T))
5+
if isinteger(x) && x != 0
6+
@test_throws DomainError Bessels.gamma(-x)
7+
else
8+
@test isapprox(T(SpecialFunctions.gamma(widen(-x))), Bessels.gamma(-x), atol=nextfloat(T(0.),2), rtol=rtol*eps(T))
9+
end
10+
end
11+
@test isnan(Bessels.gamma(T(NaN)))
12+
@test isinf(Bessels.gamma(T(Inf)))
13+
end
614

715
x = [0, 1, 2, 3, 8, 15, 20, 30]
816
@test SpecialFunctions.gamma.(x) Bessels.gamma.(x)

0 commit comments

Comments
 (0)