Skip to content

Commit 719bc91

Browse files
authored
Ensure thread-safety for trigonometric functions (#621)
1 parent 1a8ba73 commit 719bc91

File tree

1 file changed

+21
-12
lines changed

1 file changed

+21
-12
lines changed

src/intervals/arithmetic/trigonometric.jl

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,22 @@
55
# helper functions
66

77
function _quadrant(x::AbstractFloat)
8+
# NOTE: this algorithm may be flawed as it relies on `rem2pi(x, RoundNearest)`
9+
# to yield a very tight result. This is not guaranteed by Julia, see e.g.
10+
# https://github.com/JuliaLang/julia/blob/9669eecc99bc4553e28d94d7dd3dc9fd40b3bf3f/base/mpfr.jl#L845-L846
11+
PI_LO, PI_HI = bounds(bareinterval(typeof(x), π))
812
r = rem2pi(x, RoundNearest)
9-
-2r > π && return 2 # [-π, -π/2)
10-
r < 0 && return 3 # [-π/2, 0)
11-
2r < π && return 0 # [0, π/2)
13+
r2 = 2r # should be exact for floats
14+
r2 -PI_HI && return 2 # [-π, -π/2)
15+
r2 < -PI_LO && return throw(ArgumentError("could not determine the quadrant, the remainder $r of the division of $x by 2π is lesser or greater than -π/2"))
16+
r2 < 0 && return 3 # [-π/2, 0)
17+
r2 PI_LO && return 0 # [0, π/2)
18+
r2 < PI_HI && return throw(ArgumentError("could not determine the quadrant, the remainder $r of the division of $x by 2π is lesser or greater than π/2"))
1219
return 1 # [π/2, π]
1320
end
1421

1522
function _quadrantpi(x::AbstractFloat) # used in `sinpi` and `cospi`
16-
r = rem(x, 2)
23+
r = rem(x, 2) # [-2, 2], should be exact for floats
1724
2r < -3 && return 0 # [-2π, -3π/2)
1825
r < -1 && return 1 # [-3π/2, -π)
1926
2r < -1 && return 2 # [-π, -π/2)
@@ -52,16 +59,17 @@ Implement the `sin` function of the IEEE Standard 1788-2015 (Table 9.1).
5259
function Base.sin(x::BareInterval{T}) where {T<:AbstractFloat}
5360
isempty_interval(x) && return x
5461

62+
PI_HI = sup(bareinterval(T, π))
5563
d = diam(x)
56-
d/2 π && return _unsafe_bareinterval(T, -one(T), one(T))
64+
d 2PI_HI && return _unsafe_bareinterval(T, -one(T), one(T))
5765

5866
lo, hi = bounds(x)
5967

6068
lo_quadrant = _quadrant(lo)
6169
hi_quadrant = _quadrant(hi)
6270

6371
if lo_quadrant == hi_quadrant
64-
d π && return _unsafe_bareinterval(T, -one(T), one(T))
72+
d PI_HI && return _unsafe_bareinterval(T, -one(T), one(T))
6573
(lo_quadrant == 1) | (lo_quadrant == 2) && return @round(T, sin(hi), sin(lo)) # decreasing
6674
return @round(T, sin(lo), sin(hi))
6775

@@ -148,16 +156,17 @@ Implement the `cos` function of the IEEE Standard 1788-2015 (Table 9.1).
148156
function Base.cos(x::BareInterval{T}) where {T<:AbstractFloat}
149157
isempty_interval(x) && return x
150158

159+
PI_HI = sup(bareinterval(T, π))
151160
d = diam(x)
152-
d/2 π && return _unsafe_bareinterval(T, -one(T), one(T))
161+
d 2PI_HI && return _unsafe_bareinterval(T, -one(T), one(T))
153162

154163
lo, hi = bounds(x)
155164

156165
lo_quadrant = _quadrant(lo)
157166
hi_quadrant = _quadrant(hi)
158167

159168
if lo_quadrant == hi_quadrant
160-
d π && return _unsafe_bareinterval(T, -one(T), one(T))
169+
d PI_HI && return _unsafe_bareinterval(T, -one(T), one(T))
161170
(lo_quadrant == 2) | (lo_quadrant == 3) && return @round(T, cos(lo), cos(hi)) # increasing
162171
return @round(T, cos(hi), cos(lo))
163172

@@ -246,7 +255,7 @@ Implement the `tan` function of the IEEE Standard 1788-2015 (Table 9.1).
246255
function Base.tan(x::BareInterval{T}) where {T<:AbstractFloat}
247256
isempty_interval(x) && return x
248257

249-
diam(x) > π && return entireinterval(BareInterval{T})
258+
diam(x) > sup(bareinterval(T, π)) && return entireinterval(BareInterval{T})
250259

251260
lo, hi = bounds(x)
252261

@@ -282,7 +291,7 @@ Implement the `cot` function of the IEEE Standard 1788-2015 (Table 9.1).
282291
function Base.cot(x::BareInterval{T}) where {T<:AbstractFloat}
283292
isempty_interval(x) && return x
284293

285-
diam(x) > π && return entireinterval(BareInterval{T})
294+
diam(x) > sup(bareinterval(T, π)) && return entireinterval(BareInterval{T})
286295

287296
isthinzero(x) && return emptyinterval(BareInterval{T})
288297

@@ -316,7 +325,7 @@ Implement the `sec` function of the IEEE Standard 1788-2015 (Table 9.1).
316325
function Base.sec(x::BareInterval{T}) where {T<:AbstractFloat}
317326
isempty_interval(x) && return x
318327

319-
diam(x) > π && return entireinterval(BareInterval{T})
328+
diam(x) > sup(bareinterval(T, π)) && return entireinterval(BareInterval{T})
320329

321330
lo, hi = bounds(x)
322331

@@ -352,7 +361,7 @@ Implement the `csc` function of the IEEE Standard 1788-2015 (Table 9.1).
352361
function Base.csc(x::BareInterval{T}) where {T<:AbstractFloat}
353362
isempty_interval(x) && return x
354363

355-
diam(x) > π && return entireinterval(BareInterval{T})
364+
diam(x) > sup(bareinterval(T, π)) && return entireinterval(BareInterval{T})
356365

357366
isthinzero(x) && return emptyinterval(BareInterval{T})
358367

0 commit comments

Comments
 (0)