Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use rem2pi and rem with BigFloats in _quadrant #633

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 17 additions & 6 deletions src/intervals/arithmetic/trigonometric.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,23 @@
# in Section 10.5.3

# helper functions
# NOTE: According to J-M Muller, "Elementary Functions: Algorithms and
# implementations", Birkhäuser, 3rd ed (2005), chap. 11, pp 203,
# "The first solution consists of using multiple-precision arithmetic,
# but this may make the computation significantly slower. Moreover,
# it is not that easy to predict the accuracy with which the computation
# should be performed." Later (pp 210), it estimates that the precision needed
# to compute correctly the worst case of the range reduction algorithm
# for Float64 is about 61 "guard digits". Below we use `rem2pi` for
# BigFloats, with at least 200 bits of precision.
# (As noted before, `rem2pi(x, RoundNearest)`, with Float64 indeed may have inaccuracies.)
#
# For consistency, `_quadrantpi` uses `rem(big(x), 2, RoundNearest)`

function _quadrant(x::AbstractFloat)
# NOTE: this algorithm may be flawed as it relies on `rem2pi(x, RoundNearest)`
# to yield a very tight result. This is not guaranteed by Julia, see e.g.
# https://github.com/JuliaLang/julia/blob/9669eecc99bc4553e28d94d7dd3dc9fd40b3bf3f/base/mpfr.jl#L845-L846
PI_LO, PI_HI = bounds(bareinterval(typeof(x), π))
r = rem2pi(x, RoundNearest)
@assert precision(BigFloat) > 200
PI_LO, PI_HI = bounds(bareinterval(BigFloat, π))
r = rem2pi(big(x), RoundNearest)
r2 = 2r # should be exact for floats
r2 ≤ -PI_HI && return 2 # [-π, -π/2)
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"))
Expand All @@ -20,7 +30,8 @@ function _quadrant(x::AbstractFloat)
end

function _quadrantpi(x::AbstractFloat) # used in `sinpi` and `cospi`
r = rem(x, 2) # [-2, 2], should be exact for floats
@assert precision(BigFloat) > 200
r = rem(big(x), 2, RoundNearest) # [-2, 2], should be exact for floats
2r < -3 && return 0 # [-2π, -3π/2)
r < -1 && return 1 # [-3π/2, -π)
2r < -1 && return 2 # [-π, -π/2)
Expand Down
12 changes: 12 additions & 0 deletions test/interval_tests/trigonometric.jl
Original file line number Diff line number Diff line change
Expand Up @@ -307,4 +307,16 @@ end
@test isequal_interval(sin(x), interval(-1, 1))
@test isequal_interval(cos(x), interval(-1, 1))
@test isequal_interval(tan(x), interval(-Inf, Inf))

setprecision(100) do
y = 6381956970095103 * 2.0^797 # worst case for C = pi/2 for Float64
yb = 6381956970095103 * big(2.0)^797
@test_throws AssertionError IntervalArithmetic._quadrant(y) == IntervalArithmetic._quadrant(yb)
end
setprecision(1000) do
y = 6381956970095103 * 2.0^797 # worst case for C = pi/2 for Float64
yb = 6381956970095103 * big(2.0)^797
@test IntervalArithmetic._quadrant(y) == IntervalArithmetic._quadrant(yb)
end

end
Loading