Skip to content

Commit 9d40ff3

Browse files
authored
Speed up sin and cos by a factor 6.5 (#117)
* Speed up trig by a few times * Fix sin(x::BigFloat) * Fix sin for big interval * Revert tan to use previous find_quadrants
1 parent 801b6c5 commit 9d40ff3

File tree

1 file changed

+34
-7
lines changed

1 file changed

+34
-7
lines changed

src/intervals/trigonometric.jl

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,21 @@
11
# This file is part of the IntervalArithmetic.jl package; MIT licensed
22

3-
half_pi(::Type{T}) where T = pi_interval(T) / 2
4-
half_pi(x::T) where T<:AbstractFloat = half_pi(T)
53

6-
two_pi(::Type{T}) where T = pi_interval(T) * 2
4+
# hard code this for efficiency:
5+
const one_over_half_pi_interval = inv(0.5 * pi_interval(Float64))
6+
7+
"""
8+
Multiply an interval by a positive constant.
9+
For efficiency, does not check that the constant is positive.
10+
"""
11+
multiply_by_positive_constant(α, x::Interval) = @round*x.lo, α*x.hi)
12+
13+
half_pi(::Type{Float64}) = multiply_by_positive_constant(0.5, pi_interval(Float64))
14+
half_pi(::Type{T}) where {T} = 0.5 * pi_interval(T)
15+
half_pi(x::T) where {T<:AbstractFloat} = half_pi(T)
16+
17+
two_pi(::Type{Float64}) = multiply_by_positive_constant(2.0, pi_interval(Float64))
18+
two_pi(::Type{T}) where {T} = 2 * pi_interval(T)
719

820
range_atan2(::Type{T}) where {T<:Real} = Interval(-(pi_interval(T).hi), pi_interval(T).hi)
921
half_range_atan2(::Type{T}) where {T} = (temp = half_pi(T); Interval(-(temp.hi), temp.hi) )
@@ -19,9 +31,17 @@ This is a rather indirect way to determine if π/2 and 3π/2 are contained
1931
in the interval; cf. the formula for sine of an interval in
2032
Tucker, *Validated Numerics*."""
2133

22-
function find_quadrants(x::T) where {T<:AbstractFloat}
34+
function find_quadrants(x::T) where {T}
2335
temp = atomic(Interval{T}, x) / half_pi(x)
24-
(floor(temp.lo), floor(temp.hi))
36+
37+
return SVector(floor(temp.lo), floor(temp.hi))
38+
end
39+
40+
function find_quadrants(x::Float64)
41+
temp = multiply_by_positive_constant(x, one_over_half_pi_interval)
42+
# x / half_pi(Float64)
43+
44+
return SVector(floor(temp.lo), floor(temp.hi))
2545
end
2646

2747
function sin(a::Interval{T}) where T
@@ -31,6 +51,8 @@ function sin(a::Interval{T}) where T
3151

3252
diam(a) > two_pi(T).lo && return whole_range
3353

54+
# The following is equiavlent to doing temp = a / half_pi and
55+
# taking floor(a.lo), floor(a.hi)
3456
lo_quadrant = minimum(find_quadrants(a.lo))
3557
hi_quadrant = maximum(find_quadrants(a.hi))
3658

@@ -109,14 +131,19 @@ function cos(a::Interval{T}) where T
109131
end
110132
end
111133

134+
function find_quadrants_tan(x::T) where {T}
135+
temp = atomic(Interval{T}, x) / half_pi(x)
136+
137+
return SVector(floor(temp.lo), floor(temp.hi))
138+
end
112139

113140
function tan(a::Interval{T}) where T
114141
isempty(a) && return a
115142

116143
diam(a) > pi_interval(T).lo && return entireinterval(a)
117144

118-
lo_quadrant = minimum(find_quadrants(a.lo))
119-
hi_quadrant = maximum(find_quadrants(a.hi))
145+
lo_quadrant = minimum(find_quadrants_tan(a.lo))
146+
hi_quadrant = maximum(find_quadrants_tan(a.hi))
120147

121148
lo_quadrant_mod = mod(lo_quadrant, 2)
122149
hi_quadrant_mod = mod(hi_quadrant, 2)

0 commit comments

Comments
 (0)