diff --git a/docs/src/manual/configuration.md b/docs/src/manual/configuration.md index f73f7e07..08fd2a8d 100644 --- a/docs/src/manual/configuration.md +++ b/docs/src/manual/configuration.md @@ -7,7 +7,12 @@ CurrentModule = IntervalArithmetic The IntervalArithmetic.jl package provides a [`configure`](@ref) function (not exported) that allows users to fine-tune certain aspects of the package’s behavior. This is particularly useful for controlling trade-offs between computational speed and rigor. !!! warning - The [`configure`](@ref) function redefines methods that alter the internal behavior of IntervalArithmetic. This persists across the current Julia session and affect all subsequent interval arithmetic computations. + The [`configure`](@ref) function redefines methods that alter the internal behavior of IntervalArithmetic. This persists across the current Julia session and affects all subsequent interval arithmetic computations. + + A new configuration takes effect in code that starts running after `configure` returns to the Julia prompt. Code that is already running keeps using the previous configuration (see the section on world age in the Julia manual). + In particular, calling `configure` from a function leaves the old behavior intact while the rest of the function runs. Use `Base.invokelatest` to call into the new configuration from the same function. + + Each change invalidates every compiled method that depends on the modified option, so configuration changes may trigger recompilation delays before using the new configuration. Each keyword argument sets a specific configuration option: - `numtype`: control the default numerical type used to represent the bounds of the intervals. diff --git a/src/intervals/construction.jl b/src/intervals/construction.jl index e05b173a..2ae2e874 100644 --- a/src/intervals/construction.jl +++ b/src/intervals/construction.jl @@ -101,15 +101,18 @@ for irr ∈ (:(:π), :(:γ), :(:catalan)) # irrationals supported by MPFR @eval _round(::Type{T}, a::Irrational{$irr}, r::RoundingMode) where {T<:NumTypes} = __round(T, BigFloat(a, r), r) end -# irrationals not supported by MPFR, use their exact formula ℯ = exp(1), φ = (1+sqrt(5))/2 -_round(::Type{T}, ::Irrational{:ℯ}, r::RoundingMode{:Down}) where {T<:NumTypes} = - __round(T, inf(exp(bareinterval(BigFloat, 1))), r) -_round(::Type{T}, ::Irrational{:ℯ}, r::RoundingMode{:Up}) where {T<:NumTypes} = - __round(T, sup(exp(bareinterval(BigFloat, 1))), r) -_round(::Type{T}, ::Irrational{:φ}, r::RoundingMode{:Down}) where {T<:NumTypes} = - __round(T, inf((bareinterval(BigFloat, 1) + sqrt(bareinterval(BigFloat, 5))) / bareinterval(BigFloat, 2)), r) -_round(::Type{T}, ::Irrational{:φ}, r::RoundingMode{:Up}) where {T<:NumTypes} = - __round(T, sup((bareinterval(BigFloat, 1) + sqrt(bareinterval(BigFloat, 5))) / bareinterval(BigFloat, 2)), r) +# irrationals not supported by MPFR, use their exact formula ℯ = exp(1), φ = (1+sqrt(5))/2. +# The rounding mode is fixed to `:correct` rather than taken from `default_rounding()`: +# the enclosure of a constant must be rigorous whatever the configuration, and the +# generated `bareinterval(::Type{T}, ::AbstractIrrational)` bakes the result in at its +# first generation, which method invalidation does not track. +_round(::Type{T}, ::Irrational{:ℯ}, r::RoundingMode) where {T<:NumTypes} = + __round(T, _fround(exp, IntervalRounding{:correct}(), one(BigFloat), r), r) +function _round(::Type{T}, ::Irrational{:φ}, r::RoundingMode) where {T<:NumTypes} + ir = IntervalRounding{:correct}() + sqrt5 = _fround(sqrt, ir, BigFloat(5), r) + return __round(T, _fround(/, ir, _fround(+, ir, one(BigFloat), sqrt5, r), BigFloat(2), r), r) +end # floats __round(::Type{T}, a, r::RoundingMode) where {T<:AbstractFloat} = T(a, r) # rationals diff --git a/test/IntervalArithmetic.jl b/test/IntervalArithmetic.jl index 393469a9..52025f91 100644 --- a/test/IntervalArithmetic.jl +++ b/test/IntervalArithmetic.jl @@ -309,5 +309,17 @@ end @test nextfloat(inf(z128)) == sup(z128) end + # irrational enclosures are rigorous whatever the configured rounding mode + try + IntervalArithmetic.configure(rounding = :none) + for irr ∈ (MathConstants.ℯ, MathConstants.golden), T ∈ (Float64, BigFloat) + y = Base.invokelatest(bareinterval, T, irr) + @test in_interval(irr, y) + @test inf(y) < sup(y) + end + finally + IntervalArithmetic.configure(rounding = :correct) + end + @test_throws ArgumentError("only irrationals from MathConstants or IrrationalConstants.jl are supported") bareinterval(Float64, Base.Irrational{:foo}()) end