Skip to content

Commit f864b22

Browse files
musmstevengj
authored andcommitted
Add pure julia exp function (#19831)
* Add pure julia exp function * Add a few more ldexp tests
1 parent 0b8b6f1 commit f864b22

File tree

6 files changed

+224
-50
lines changed

6 files changed

+224
-50
lines changed

LICENSE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ The following components of Julia's standard library have separate licenses:
7373
- base/fftw.jl (see [FFTW](http://fftw.org/doc/License-and-Copyright.html))
7474
- base/linalg/umfpack.jl (see [SUITESPARSE](http://faculty.cse.tamu.edu/davis/suitesparse.html))
7575
- base/linalg/cholmod.jl (see [SUITESPARSE](http://faculty.cse.tamu.edu/davis/suitesparse.html))
76+
- base/special/exp.jl (see [FREEBSD MSUN](https://github.com/freebsd/freebsd) [FreeBSD/2-clause BSD/Simplified BSD License])
7677

7778

7879
Julia's build process uses the following external tools:

base/float.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -738,9 +738,18 @@ exponent_one(::Type{Float16}) = 0x3c00
738738
exponent_half(::Type{Float16}) = 0x3800
739739
significand_mask(::Type{Float16}) = 0x03ff
740740

741+
# integer size of float
742+
fpinttype(::Type{Float64}) = UInt64
743+
fpinttype(::Type{Float32}) = UInt32
744+
fpinttype(::Type{Float16}) = UInt16
745+
741746
@pure significand_bits{T<:AbstractFloat}(::Type{T}) = trailing_ones(significand_mask(T))
742747
@pure exponent_bits{T<:AbstractFloat}(::Type{T}) = sizeof(T)*8 - significand_bits(T) - 1
743748
@pure exponent_bias{T<:AbstractFloat}(::Type{T}) = Int(exponent_one(T) >> significand_bits(T))
749+
# maximum float exponent
750+
@pure exponent_max{T<:AbstractFloat}(::Type{T}) = Int(exponent_mask(T) >> significand_bits(T)) - exponent_bias(T)
751+
# maximum float exponent without bias
752+
@pure exponent_raw_max{T<:AbstractFloat}(::Type{T}) = Int(exponent_mask(T) >> significand_bits(T))
744753

745754
## Array operations on floating point numbers ##
746755

base/math.jl

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,13 @@ export sin, cos, tan, sinh, cosh, tanh, asin, acos, atan,
2626
import Base: log, exp, sin, cos, tan, sinh, cosh, tanh, asin,
2727
acos, atan, asinh, acosh, atanh, sqrt, log2, log10,
2828
max, min, minmax, ^, exp2, muladd,
29-
exp10, expm1, log1p,
30-
sign_mask, exponent_mask, exponent_one, exponent_half,
31-
significand_mask, significand_bits, exponent_bits, exponent_bias
29+
exp10, expm1, log1p
3230

31+
using Base: sign_mask, exponent_mask, exponent_one, exponent_bias,
32+
exponent_half, exponent_max, exponent_raw_max, fpinttype,
33+
significand_mask, significand_bits, exponent_bits
3334

34-
import Core.Intrinsics: sqrt_llvm, box, unbox, powi_llvm
35+
using Core.Intrinsics: sqrt_llvm, box, unbox, powi_llvm
3536

3637
# non-type specific math functions
3738

@@ -222,13 +223,6 @@ Compute the inverse hyperbolic sine of `x`.
222223
"""
223224
asinh(x)
224225

225-
"""
226-
exp(x)
227-
228-
Compute ``e^x``.
229-
"""
230-
exp(x)
231-
232226
"""
233227
erf(x)
234228
@@ -250,13 +244,16 @@ erfc(x)
250244
Accurately compute ``e^x-1``.
251245
"""
252246
expm1(x)
253-
for f in (:cbrt, :sinh, :cosh, :tanh, :atan, :asinh, :exp, :erf, :erfc, :exp2, :expm1)
247+
for f in (:cbrt, :sinh, :cosh, :tanh, :atan, :asinh, :erf, :erfc, :exp2, :expm1)
254248
@eval begin
255249
($f)(x::Float64) = ccall(($(string(f)),libm), Float64, (Float64,), x)
256250
($f)(x::Float32) = ccall(($(string(f,"f")),libm), Float32, (Float32,), x)
257251
($f)(x::Real) = ($f)(float(x))
258252
end
259253
end
254+
# pure julia exp function
255+
include("special/exp.jl")
256+
exp(x::Real) = exp(float(x))
260257

261258
# fallback definitions to prevent infinite loop from $f(x::Real) def above
262259

@@ -530,11 +527,11 @@ function ldexp{T<:AbstractFloat}(x::T, e::Integer)
530527
n = e % Int
531528
k += n
532529
# overflow, if k is larger than maximum posible exponent
533-
if k >= Int(exponent_mask(T) >> significand_bits(T))
530+
if k >= exponent_raw_max(T)
534531
return flipsign(T(Inf), x)
535532
end
536533
if k > 0 # normal case
537-
xu = (xu & ~exponent_mask(T)) | (k % typeof(xu) << significand_bits(T))
534+
xu = (xu & ~exponent_mask(T)) | (rem(k, fpinttype(T)) << significand_bits(T))
538535
return reinterpret(T, xu)
539536
else # subnormal case
540537
if k <= -significand_bits(T) # underflow
@@ -544,7 +541,7 @@ function ldexp{T<:AbstractFloat}(x::T, e::Integer)
544541
end
545542
k += significand_bits(T)
546543
z = T(2.0)^-significand_bits(T)
547-
xu = (xu & ~exponent_mask(T)) | (k % typeof(xu) << significand_bits(T))
544+
xu = (xu & ~exponent_mask(T)) | (rem(k, fpinttype(T)) << significand_bits(T))
548545
return z*reinterpret(T, xu)
549546
end
550547
end

base/special/exp.jl

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
# Based on FreeBSD lib/msun/src/e_exp.c
2+
# which is made available under the following licence
3+
4+
## Copyright (C) 2004 by Sun Microsystems, Inc. All rights reserved. Permission
5+
## to use, copy, modify, and distribute this software is freely granted,
6+
## provided that this notice is preserved.
7+
8+
# Method
9+
# 1. Argument reduction: Reduce x to an r so that |r| <= 0.5*ln(2). Given x,
10+
# find r and integer k such that
11+
# x = k*ln(2) + r, |r| <= 0.5*ln(2).
12+
# Here r is represented as r = hi - lo for better accuracy.
13+
#
14+
# 2. Approximate exp(r) by a special rational function on [0, 0.5*ln(2)]:
15+
# R(r^2) = r*(exp(r)+1)/(exp(r)-1) = 2 + r*r/6 - r^4/360 + ...
16+
#
17+
# A special Remez algorithm on [0, 0.5*ln(2)] is used to generate a
18+
# polynomial to approximate R.
19+
#
20+
# The computation of exp(r) thus becomes
21+
# 2*r
22+
# exp(r) = 1 + ----------
23+
# R(r) - r
24+
# r*c(r)
25+
# = 1 + r + ----------- (for better accuracy)
26+
# 2 - c(r)
27+
# where
28+
# c(r) = r - (P1*r^2 + P2*r^4 + ... + P5*r^10 + ...).
29+
#
30+
# 3. Scale back: exp(x) = 2^k * exp(r)
31+
32+
# log(2)
33+
const LN2 = 6.931471805599453094172321214581765680755001343602552541206800094933936219696955e-01
34+
# log2(e)
35+
const LOG2E = 1.442695040888963407359924681001892137426646
36+
37+
# log(2) into upper and lower
38+
LN2U(::Type{Float64}) = 6.93147180369123816490e-1
39+
LN2U(::Type{Float32}) = 6.9313812256f-1
40+
41+
LN2L(::Type{Float64}) = 1.90821492927058770002e-10
42+
LN2L(::Type{Float32}) = 9.0580006145f-6
43+
44+
# max and min arguments for exponential fucntions
45+
MAXEXP(::Type{Float64}) = 7.09782712893383996732e2 # log 2^1023*(2-2^-52)
46+
MAXEXP(::Type{Float32}) = 88.72283905206835f0 # log 2^127 *(2-2^-23)
47+
48+
# one less than the min exponent since we can sqeeze a bit more from the exp function
49+
MINEXP(::Type{Float64}) = -7.451332191019412076235e2 # log 2^-1075
50+
MINEXP(::Type{Float32}) = -103.97207708f0 # log 2^-150
51+
52+
@inline exp_kernel(x::Float64) = @horner(x, 1.66666666666666019037e-1,
53+
-2.77777777770155933842e-3, 6.61375632143793436117e-5,
54+
-1.65339022054652515390e-6, 4.13813679705723846039e-8)
55+
56+
@inline exp_kernel(x::Float32) = @horner(x, 1.6666625440f-1, -2.7667332906f-3)
57+
58+
# for values smaller than this threshold just use a Taylor expansion
59+
exp_small_thres(::Type{Float64}) = 2.0^-28
60+
exp_small_thres(::Type{Float32}) = 2.0f0^-13
61+
62+
"""
63+
exp(x)
64+
65+
Compute the natural base exponential of `x`, in other words ``e^x``.
66+
"""
67+
function exp{T<:Union{Float32,Float64}}(x::T)
68+
xa = reinterpret(Unsigned, x) & ~sign_mask(T)
69+
xsb = signbit(x)
70+
71+
# filter out non-finite arguments
72+
if xa > reinterpret(Unsigned, MAXEXP(T))
73+
if xa >= exponent_mask(T)
74+
xa & significand_mask(T) != 0 && return T(NaN)
75+
return xsb ? T(0.0) : T(Inf) # exp(+-Inf)
76+
end
77+
x > MAXEXP(T) && return T(Inf)
78+
x < MINEXP(T) && return T(0.0)
79+
end
80+
81+
# This implementation gives 2.7182818284590455 for exp(1.0) when T ==
82+
# Float64, which is well within the allowable error; however,
83+
# 2.718281828459045 is closer to the true value so we prefer that answer,
84+
# given that 1.0 is such an important argument value.
85+
if x == T(1.0) && T == Float64
86+
return 2.718281828459045235360
87+
end
88+
89+
if xa > reinterpret(Unsigned, T(0.5)*T(LN2)) # |x| > 0.5 log(2)
90+
# argument reduction
91+
if xa < reinterpret(Unsigned, T(1.5)*T(LN2)) # |x| < 1.5 log(2)
92+
if xsb
93+
k = -1
94+
hi = x + LN2U(T)
95+
lo = -LN2L(T)
96+
else
97+
k = 1
98+
hi = x - LN2U(T)
99+
lo = LN2L(T)
100+
end
101+
else
102+
n = round(T(LOG2E)*x)
103+
k = unsafe_trunc(Int,n)
104+
hi = muladd(n, -LN2U(T), x)
105+
lo = n*LN2L(T)
106+
end
107+
r = hi - lo
108+
109+
# compute approximation on reduced argument
110+
z = r*r
111+
p = r - z*exp_kernel(z)
112+
y = T(1.0) - ((lo - (r*p)/(T(2.0) - p)) - hi)
113+
114+
# scale back
115+
if k > -significand_bits(T)
116+
# multiply by 2.0 first to prevent overflow, which helps extends the range
117+
k == exponent_max(T) && return y*T(2.0)*T(2.0)^(exponent_max(T) - 1)
118+
twopk = reinterpret(T, rem(exponent_bias(T) + k, fpinttype(T)) << significand_bits(T))
119+
return y*twopk
120+
else
121+
# add significand_bits(T) + 1 to lift the range outside the subnormals
122+
twopk = reinterpret(T, rem(exponent_bias(T) + significand_bits(T) + 1 + k, fpinttype(T)) << significand_bits(T))
123+
return y*twopk*T(2.0)^(-significand_bits(T) - 1)
124+
end
125+
elseif xa < reinterpret(Unsigned, exp_small_thres(T)) # |x| < exp_small_thres
126+
# Taylor approximation for small x
127+
return T(1.0) + x
128+
else
129+
# primary range with k = 0, so compute approximation directly
130+
z = x*x
131+
p = x - z*exp_kernel(z)
132+
return T(1.0) - ((x*p)/(p - T(2.0)) - x)
133+
end
134+
end

contrib/add_license_to_files.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ const skipfiles = [
3434
# files to check - already copyright
3535
# see: https://github.com/JuliaLang/julia/pull/11073#issuecomment-98099389
3636
"../base/special/trig.jl",
37+
"../base/special/exp.jl"
3738
"../base/linalg/givens.jl",
3839
#
3940
"../src/abi_llvm.cpp",

test/math.jl

Lines changed: 67 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323
end
2424

2525
@testset "constants" begin
26-
@test !(pi == e)
27-
@test !(e == 1//2)
26+
@test pi != e
27+
@test e != 1//2
2828
@test 1//2 <= e
2929
@test e <= 15//3
3030
@test big(1//2) < e
@@ -34,8 +34,8 @@ end
3434
@test e^2.4 == exp(2.4)
3535
@test e^(2//3) == exp(2//3)
3636

37-
@test Float16(3.) < pi
38-
@test pi < Float16(4.)
37+
@test Float16(3.0) < pi
38+
@test pi < Float16(4.0)
3939
@test contains(sprint(show,π),"3.14159")
4040
end
4141

@@ -71,37 +71,51 @@ end
7171
@test isnan(x)
7272
@test y == 0
7373

74-
# more ldexp tests
75-
@test ldexp(T(0.8), 4) === T(12.8)
76-
@test ldexp(T(-0.854375), 5) === T(-27.34)
77-
@test ldexp(T(1.0), typemax(Int)) === T(Inf)
78-
@test ldexp(T(1.0), typemin(Int)) === T(0.0)
79-
@test ldexp(prevfloat(realmin(T)), typemax(Int)) === T(Inf)
80-
@test ldexp(prevfloat(realmin(T)), typemin(Int)) === T(0.0)
81-
82-
@test ldexp(T(0.8), Int128(4)) === T(12.8)
83-
@test ldexp(T(-0.854375), Int128(5)) === T(-27.34)
84-
@test ldexp(T(1.0), typemax(Int128)) === T(Inf)
85-
@test ldexp(T(1.0), typemin(Int128)) === T(0.0)
86-
@test ldexp(prevfloat(realmin(T)), typemax(Int128)) === T(Inf)
87-
@test ldexp(prevfloat(realmin(T)), typemin(Int128)) === T(0.0)
88-
89-
@test ldexp(T(0.8), BigInt(4)) === T(12.8)
90-
@test ldexp(T(-0.854375), BigInt(5)) === T(-27.34)
91-
@test ldexp(T(1.0), BigInt(typemax(Int128))) === T(Inf)
92-
@test ldexp(T(1.0), BigInt(typemin(Int128))) === T(0.0)
93-
@test ldexp(prevfloat(realmin(T)), BigInt(typemax(Int128))) === T(Inf)
94-
@test ldexp(prevfloat(realmin(T)), BigInt(typemin(Int128))) === T(0.0)
95-
96-
# Test also against BigFloat reference. Needs to be exactly rounded.
97-
@test ldexp(realmin(T), -1) == T(ldexp(big(realmin(T)), -1))
98-
@test ldexp(realmin(T), -2) == T(ldexp(big(realmin(T)), -2))
99-
@test ldexp(realmin(T)/2, 0) == T(ldexp(big(realmin(T)/2), 0))
100-
@test ldexp(realmin(T)/3, 0) == T(ldexp(big(realmin(T)/3), 0))
101-
@test ldexp(realmin(T)/3, -1) == T(ldexp(big(realmin(T)/3), -1))
102-
@test ldexp(realmin(T)/3, 11) == T(ldexp(big(realmin(T)/3), 11))
103-
@test ldexp(realmin(T)/11, -10) == T(ldexp(big(realmin(T)/11), -10))
104-
@test ldexp(-realmin(T)/11, -10) == T(ldexp(big(-realmin(T)/11), -10))
74+
@testset "ldexp function" begin
75+
@test ldexp(T(0.0), 0) === T(0.0)
76+
@test ldexp(T(-0.0), 0) === T(-0.0)
77+
@test ldexp(T(Inf), 1) === T(Inf)
78+
@test ldexp(T(Inf), 10000) === T(Inf)
79+
@test ldexp(T(-Inf), 1) === T(-Inf)
80+
@test ldexp(T(NaN), 10) === T(NaN)
81+
@test ldexp(T(1.0), 0) === T(1.0)
82+
@test ldexp(T(0.8), 4) === T(12.8)
83+
@test ldexp(T(-0.854375), 5) === T(-27.34)
84+
@test ldexp(T(1.0), typemax(Int)) === T(Inf)
85+
@test ldexp(T(1.0), typemin(Int)) === T(0.0)
86+
@test ldexp(prevfloat(realmin(T)), typemax(Int)) === T(Inf)
87+
@test ldexp(prevfloat(realmin(T)), typemin(Int)) === T(0.0)
88+
89+
@test ldexp(T(0.0), Int128(0)) === T(0.0)
90+
@test ldexp(T(-0.0), Int128(0)) === T(-0.0)
91+
@test ldexp(T(1.0), Int128(0)) === T(1.0)
92+
@test ldexp(T(0.8), Int128(4)) === T(12.8)
93+
@test ldexp(T(-0.854375), Int128(5)) === T(-27.34)
94+
@test ldexp(T(1.0), typemax(Int128)) === T(Inf)
95+
@test ldexp(T(1.0), typemin(Int128)) === T(0.0)
96+
@test ldexp(prevfloat(realmin(T)), typemax(Int128)) === T(Inf)
97+
@test ldexp(prevfloat(realmin(T)), typemin(Int128)) === T(0.0)
98+
99+
@test ldexp(T(0.0), BigInt(0)) === T(0.0)
100+
@test ldexp(T(-0.0), BigInt(0)) === T(-0.0)
101+
@test ldexp(T(1.0), BigInt(0)) === T(1.0)
102+
@test ldexp(T(0.8), BigInt(4)) === T(12.8)
103+
@test ldexp(T(-0.854375), BigInt(5)) === T(-27.34)
104+
@test ldexp(T(1.0), BigInt(typemax(Int128))) === T(Inf)
105+
@test ldexp(T(1.0), BigInt(typemin(Int128))) === T(0.0)
106+
@test ldexp(prevfloat(realmin(T)), BigInt(typemax(Int128))) === T(Inf)
107+
@test ldexp(prevfloat(realmin(T)), BigInt(typemin(Int128))) === T(0.0)
108+
109+
# Test also against BigFloat reference. Needs to be exactly rounded.
110+
@test ldexp(realmin(T), -1) == T(ldexp(big(realmin(T)), -1))
111+
@test ldexp(realmin(T), -2) == T(ldexp(big(realmin(T)), -2))
112+
@test ldexp(realmin(T)/2, 0) == T(ldexp(big(realmin(T)/2), 0))
113+
@test ldexp(realmin(T)/3, 0) == T(ldexp(big(realmin(T)/3), 0))
114+
@test ldexp(realmin(T)/3, -1) == T(ldexp(big(realmin(T)/3), -1))
115+
@test ldexp(realmin(T)/3, 11) == T(ldexp(big(realmin(T)/3), 11))
116+
@test ldexp(realmin(T)/11, -10) == T(ldexp(big(realmin(T)/11), -10))
117+
@test ldexp(-realmin(T)/11, -10) == T(ldexp(big(-realmin(T)/11), -10))
118+
end
105119
end
106120
end
107121

@@ -235,6 +249,24 @@ end
235249
@test exp2(Float16(2.)) exp2(2.)
236250
@test log(e) == 1
237251

252+
@testset "exp function" for T in (Float64, Float32)
253+
@testset "$T accuracy" begin
254+
X = map(T, vcat(-10:0.0002:10, -80:0.001:80, 2.0^-27, 2.0^-28, 2.0^-14, 2.0^-13))
255+
for x in X
256+
y, yb = exp(x), exp(big(x))
257+
@test abs(y-yb) <= 1.0*eps(T(yb))
258+
end
259+
end
260+
@testset "$T edge cases" begin
261+
@test isnan(exp(T(NaN)))
262+
@test exp(T(-Inf)) === T(0.0)
263+
@test exp(T(Inf)) === T(Inf)
264+
@test exp(T(0.0)) === T(1.0) # exact
265+
@test exp(T(5000.0)) === T(Inf)
266+
@test exp(T(-5000.0)) === T(0.0)
267+
end
268+
end
269+
238270
@testset "test abstractarray trig fxns" begin
239271
TAA = rand(2,2)
240272
TAA = (TAA + TAA.')/2.

0 commit comments

Comments
 (0)