diff --git a/REQUIRE b/REQUIRE index 4b77aa08a..91468cbef 100644 --- a/REQUIRE +++ b/REQUIRE @@ -1,5 +1,6 @@ julia 0.5 CRlibm 0.5 StaticArrays 0.3 -FastRounding 0.0.1 +FastRounding 0.0.4 +AdjacentFloats 0.0.4 RecipesBase diff --git a/docs/src/usage.md b/docs/src/usage.md index 89b9508db..d70aa2051 100644 --- a/docs/src/usage.md +++ b/docs/src/usage.md @@ -336,7 +336,7 @@ julia> @interval sin(1) By default, the directed rounding used corresponds to using the `RoundDown` and `RoundUp` rounding modes when performing calculations; this gives the narrowest resulting intervals, and is set by ```jldoctest usage -julia> setrounding(Interval, :tight) +julia> setrounding(Interval, :slow) ``` diff --git a/src/IntervalArithmetic.jl b/src/IntervalArithmetic.jl index b6a801d23..0cd9ef64f 100644 --- a/src/IntervalArithmetic.jl +++ b/src/IntervalArithmetic.jl @@ -7,6 +7,7 @@ module IntervalArithmetic import CRlibm using StaticArrays using FastRounding +using AdjacentFloats import Base: +, -, *, /, //, fma, diff --git a/src/intervals/rounding.jl b/src/intervals/rounding.jl index d954f3fe3..f1c003b9c 100644 --- a/src/intervals/rounding.jl +++ b/src/intervals/rounding.jl @@ -4,21 +4,21 @@ This is a so-called "traits-based" design, as follows. The main body of the file defines versions of elementary functions with all allowed interval rounding types, e.g. -+(IntervalRounding{:accurate}, a, b, RoundDown) +(IntervalRounding{:tight}, a, b, RoundDown) +(IntervalRounding{:accurate}, a, b, RoundDown) ++(IntervalRounding{:slow}, a, b, RoundDown) +(IntervalRounding{:none}, a, b, RoundDown) The current allowed rounding types are -- :accurate # fast, tight (correct) rounding with errorfree arithmetic via FastRounding.jl -- :tight # tight (correct) rounding by changing rounding mode (slow) +- :tight # fast, tight (correct) rounding with errorfree arithmetic via FastRounding.jl - :accurate # fast "accurate" rounding using prevfloat and nextfloat (slightly wider than needed) +- :slow # tight (correct) rounding by changing rounding mode (slow) - :none # no rounding (for speed comparisons; no enclosure is guaranteed) The function `setrounding(Interval, rounding_type)` then defines rounded functions *without* an explicit rounding type, e.g. -sin(x, r::RoundingMode) = sin(IntervalRounding{:tight}, x, r) +sin(x, r::RoundingMode) = sin(IntervalRounding{:slow}, x, r) These are overwritten when `setrounding(Interval, rounding_type)` is called again. @@ -74,7 +74,7 @@ for (op, f) in ( (:+, :add), (:-, :sub), (:*, :mul), (:/, :div) ) mode2 = Symbol("Round", mode) - @eval $op(::IntervalRounding{:fast}, + @eval $op(::IntervalRounding{:tight}, a::$T, b::$T, $mode1) = $ff(a, b, $mode2) end end @@ -88,10 +88,10 @@ for T in (Float32, Float64) mode2 = Symbol("Round", mode) - @eval inv(::IntervalRounding{:fast}, + @eval inv(::IntervalRounding{:tight}, a::$T, $mode1) = inv_round(a, $mode2) - @eval sqrt(::IntervalRounding{:fast}, + @eval sqrt(::IntervalRounding{:tight}, a::$T, $mode1) = sqrt_round(a, $mode2) end end @@ -106,22 +106,22 @@ for mode in (:Down, :Up) mode2 = Symbol("Round", mode) if mode == :Down - directed = :prevfloat + directed = :prev_float else - directed = :nextfloat + directed = :next_float end # binary functions: for f in (:+, :-, :*, :/, :atan2) - @eval function $f{T<:AbstractFloat}(::IntervalRounding{:tight}, + @eval function $f{T<:AbstractFloat}(::IntervalRounding{:slow}, a::T, b::T, $mode1) setrounding(T, $mode2) do $f(a, b) end end - @eval function $f{T<:AbstractFloat}(::IntervalRounding{:fast}, + @eval function $f{T<:AbstractFloat}(::IntervalRounding{:tight}, a::T, b::T, $mode1) setrounding(T, $mode2) do $f(a, b) @@ -139,7 +139,7 @@ for mode in (:Down, :Up) # power: - @eval function ^{S<:Real}(::IntervalRounding{:tight}, + @eval function ^{S<:Real}(::IntervalRounding{:slow}, a::BigFloat, b::S, $mode1) setrounding(BigFloat, $mode2) do ^(a, b) @@ -147,9 +147,9 @@ for mode in (:Down, :Up) end # for correct rounding for Float64, must pass through BigFloat: - @eval function ^{S<:Real}(::IntervalRounding{:tight}, a::Float64, b::S, $mode1) + @eval function ^{S<:Real}(::IntervalRounding{:slow}, a::Float64, b::S, $mode1) setprecision(BigFloat, 53) do - Float64(^(IntervalRounding{:tight}, BigFloat(a), b, $mode2)) + Float64(^(IntervalRounding{:slow}, BigFloat(a), b, $mode2)) end end @@ -163,7 +163,7 @@ for mode in (:Down, :Up) # functions not in CRlibm: for f in (:sqrt, :inv, :tanh, :asinh, :acosh, :atanh) - @eval function $f{T<:AbstractFloat}(::IntervalRounding{:tight}, + @eval function $f{T<:AbstractFloat}(::IntervalRounding{:slow}, a::T, $mode1) setrounding(T, $mode2) do $f(a) @@ -184,7 +184,7 @@ for mode in (:Down, :Up) # Functions defined in CRlibm for f in CRlibm.functions - @eval $f{T<:AbstractFloat}(::IntervalRounding{:tight}, + @eval $f{T<:AbstractFloat}(::IntervalRounding{:slow}, a::T, $mode1) = CRlibm.$f(a, $mode2) @eval $f{T<:AbstractFloat}(::IntervalRounding{:accurate}, @@ -197,14 +197,16 @@ for mode in (:Down, :Up) end +const rounding_types = (:tight, :accurate, :slow, :none) + function _setrounding(::Type{Interval}, rounding_type::Symbol) if rounding_type == current_rounding_type[] return # no need to redefine anything end - if rounding_type ∉ (:fast, :tight, :accurate, :none) - throw(ArgumentError("Rounding type must be one of `:fast`, `:tight`, `:accurate`, `:none`")) + if rounding_type ∉ rounding_types + throw(ArgumentError("Rounding type must be one of $rounding_types")) end roundtype = IntervalRounding{rounding_type}() @@ -215,8 +217,8 @@ function _setrounding(::Type{Interval}, rounding_type::Symbol) @eval $f{T<:AbstractFloat}(a::T, b::T, r::RoundingMode) = $f($roundtype, a, b, r) end - if rounding_type == :fast # for remaining functions, use CRlibm - roundtype = IntervalRounding{:tight}() + if rounding_type == :tight # for remaining functions, use CRlibm + roundtype = IntervalRounding{:slow}() end for f in (:^, :atan2) @@ -243,7 +245,7 @@ doc""" setrounding(Interval, rounding_type::Symbol) Set the rounding type used for all interval calculations on Julia v0.6 and above. -Valid `rounding_type`s are `:tight`, `:accurate` and `:none`, `:fast`. +Valid `rounding_type`s are $rounding_types. """ function setrounding(::Type{Interval}, rounding_type::Symbol) @@ -279,4 +281,4 @@ end # default: correct rounding const current_rounding_type = Symbol[:undefined] -setrounding(Interval, :fast) +setrounding(Interval, :tight) diff --git a/test/interval_tests/rounding.jl b/test/interval_tests/rounding.jl index 9b765835f..e1fa82289 100644 --- a/test/interval_tests/rounding.jl +++ b/test/interval_tests/rounding.jl @@ -8,7 +8,7 @@ setformat(:full) # NB: Due to "world age" problems, the following is not a @testset -setrounding(Interval, :tight) +setrounding(Interval, :slow) x = Interval(0.5) @testset "Correct rounding" begin @test sin(x) == Interval(0.47942553860420295, 0.479425538604203) @@ -24,12 +24,12 @@ setrounding(Interval, :none) @test sin(x) == Interval(0.479425538604203, 0.479425538604203) end -setrounding(Interval, :tight) +setrounding(Interval, :slow) @testset "Back to correct rounding" begin @test sin(x) == Interval(0.47942553860420295, 0.479425538604203) end -setrounding(Interval, :fast) +setrounding(Interval, :tight) @testset "Back to error-free rounding" begin @test sin(x) == Interval(0.47942553860420295, 0.479425538604203) end