Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
fail-fast: false
matrix:
version:
- '1.9'
- '1.10'
- '1'
- 'nightly'
os:
Expand Down
12 changes: 7 additions & 5 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ CRlibm = "96374032-68de-5a5b-8d9e-752f78720389"
MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09"
OpenBLASConsistentFPCSR_jll = "6cdc7f73-28fd-5e50-80fb-958a8875b1af"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
RoundingEmulator = "5eaf0fd0-dfba-4ccb-bf02-d820a40db705"

[weakdeps]
Expand All @@ -32,13 +33,14 @@ IntervalArithmeticSparseArraysExt = "SparseArrays"
Arblib = "1.3.0"
CRlibm = "1.0.2"
DiffRules = "1"
ForwardDiff = "0.10, 1"
ForwardDiff = "1"
IntervalSets = "0.7"
LinearAlgebra = "1.9"
LinearAlgebra = "1.10"
MacroTools = "0.5"
OpenBLASConsistentFPCSR_jll = "0.3.29"
Random = "1.9"
Random = "1.10"
Printf = "1.10"
RecipesBase = "1"
RoundingEmulator = "0.2"
SparseArrays = "1.9.0"
julia = "1.9"
SparseArrays = "1.10"
julia = "1.10"
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ The official documentation is available online: https://juliaintervals.github.io

## Installation

The IntervalArithmetic.jl package requires to [install Julia](https://julialang.org/downloads/) (v1.9 or above).
The IntervalArithmetic.jl package requires to [install Julia](https://julialang.org/downloads/) (v1.10 or above).

Then, start Julia and execute the following command in the REPL:

Expand Down
2 changes: 1 addition & 1 deletion docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ IntervalArithmetic.jl is a Julia package for validated numerics in Julia. All ca
## Installation

```@repl
using Pkg # Julia v1.9 or above
using Pkg # Julia v1.10 or above
redirect_stderr(devnull) do # hide
Pkg.add("IntervalArithmetic")
end # hide
Expand Down
9 changes: 5 additions & 4 deletions ext/IntervalArithmeticForwardDiffExt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ Base.promote_rule(::Type{ExactReal{S}}, ::Type{Dual{T, V, N}}) where {S<:Real, T
Base.promote_rule(::Type{Dual{T, V, N}}, ::Type{ExactReal{S}}) where {S<:Real, T, V, N} =
Dual{T,ExactReal{IntervalArithmetic.promote_numtype(V, S)},N}

Base.:(==)(x::Union{BareInterval,Interval}, y::Dual) = x == value(y)
Base.:(==)(x::Dual, y::Union{BareInterval,Interval}) = value(x) == y
Base.:(==)(x::Interval, y::Dual) = x == value(y)
Base.:(==)(x::Dual, y::Interval) = value(x) == y
Base.:<(x::Interval, y::Dual) = x < value(y)
Base.:<(x::Dual, y::Interval) = value(x) < y

function Base.:(^)(x::Dual{Txy,<:Interval}, y::Dual{Txy,<:Interval}) where {Txy}
vx, vy = value(x), value(y)
Expand Down Expand Up @@ -91,10 +93,9 @@ function Base.:(^)(x::ExactReal, y::Dual{<:Ty}) where {Ty}
end
end


# Piecewise functions

function (constant::Constant)(::Dual{T, Interval{S}}) where {T, S}
function (constant::Constant)(::Dual{T,Interval{S}}) where {T, S}
return Dual{T}(interval(S, constant.value), interval(S, 0.0))
end

Expand Down
4 changes: 3 additions & 1 deletion src/IntervalArithmetic.jl
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ include("piecewise.jl")

#

import Printf

include("display.jl")
export setdisplay

Expand Down Expand Up @@ -228,7 +230,7 @@ Configure the default behavior for:
[`IntervalArithmetic.MatMulMode`](@ref).
Keyword: `matmul`. Available options: `:fast` (default), `:slow`.
"""
function configure(; numtype::Type{<:NumTypes}=Float64, flavor::Symbol=:set_based, rounding::Symbol=:correct, power::Symbol=:fast, matmul::Symbol=:slow)
function configure(; numtype::Type{<:NumTypes}=Float64, flavor::Symbol=:set_based, rounding::Symbol=:correct, power::Symbol=:fast, matmul::Symbol=:fast)
configure_numtype(numtype)
configure_flavor(flavor)
configure_rounding(rounding)
Expand Down
133 changes: 25 additions & 108 deletions src/display.jl
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ Display options:
- significant digits: 6

julia> x = interval(0.1, 0.3)
[0.0999999, 0.300001]_com
[0.1, 0.3]_com

julia> setdisplay(:full)
Display options:
Expand All @@ -69,7 +69,7 @@ Display options:
- significant digits: 3

julia> x
[0.0999, 0.301]_com
[0.1, 0.3]_com

julia> setdisplay(; decorations = false)
Display options:
Expand All @@ -79,7 +79,7 @@ Display options:
- significant digits: 3

julia> x
[0.0999, 0.301]
[0.1, 0.3]
```
"""
function setdisplay(format::Symbol = display_options.format;
Expand Down Expand Up @@ -196,14 +196,14 @@ function _str_basic_repr(a::BareInterval{<:AbstractFloat}, format::Symbol)
return string(str_lo, ", ", str_hi)
elseif format === :midpoint
m = mid(a)
str_m = _round_string(m, sigdigits, RoundNearest)
str_m = _round_string(m, sigdigits)
# str_m = ifelse(m ≥ 0, string('+', str_m), str_m)
output = string(str_m, " ± ", _round_string(radius(a), sigdigits, RoundUp))
output = string(str_m, " ± ", _round_string(radius(a), sigdigits))
return replace(output, "Inf" => '∞')
else
str_lo = _round_string(lo, sigdigits, RoundDown)
str_lo = _round_string(lo, sigdigits)
# str_lo = ifelse(lo ≥ 0, string('+', str_lo), str_lo)
str_hi = _round_string(hi, sigdigits, RoundUp)
str_hi = _round_string(hi, sigdigits)
# str_hi = ifelse(hi ≥ 0, string('+', str_hi), str_hi)
output = string('[', str_lo, ", ", str_hi, ']')
return replace(output, "Inf]" => "∞)", "[-Inf" => "(-∞")
Expand Down Expand Up @@ -231,26 +231,26 @@ function _str_basic_repr(a::BareInterval{Float32}, format::Symbol)
return string(str_lo, ", ", str_hi)
elseif format === :midpoint
m = mid(a)
str_m = _round_string(m, sigdigits, RoundNearest)
str_m = _round_string(m, sigdigits)
str_m = replace(string(str_m, "f0"), "NaNf0" => "NaN32", "Inff0" => "Inf32")
if contains(str_m, 'e')
str_m = replace(str_m, 'e' => 'f', "f0" => "")
end
# str_m = ifelse(m ≥ 0, string('+', str_m), str_m)
str_r = _round_string(radius(a), sigdigits, RoundUp)
str_r = _round_string(radius(a), sigdigits)
str_r = replace(string(str_r, "f0"), "NaNf0" => "NaN32", "Inff0" => "Inf32")
if contains(str_r, 'e')
str_r = replace(str_r, 'e' => 'f', "f0" => "")
end
return string(str_m, " ± ", str_r)
else
str_lo = _round_string(lo, sigdigits, RoundDown)
str_lo = _round_string(lo, sigdigits)
str_lo = replace(string('[', str_lo, "f0"), "NaNf0" => "NaN32", "[-Inff0" => "(-∞")
if contains(str_lo, 'e')
str_lo = replace(str_lo, 'e' => 'f', "f0" => "")
end
# str_lo = ifelse(lo ≥ 0, string('+', str_lo), str_lo)
str_hi = _round_string(hi, sigdigits, RoundUp)
str_hi = _round_string(hi, sigdigits)
str_hi = replace(string(str_hi, "f0]"), "NaNf0" => "NaN32", "Inff0]" => "∞)")
if contains(str_hi, 'e')
str_hi = replace(str_hi, 'e' => 'f', "f0" => "")
Expand All @@ -274,14 +274,14 @@ function _str_basic_repr(a::BareInterval{Float16}, format::Symbol)
return replace(output, "Float16(NaN)" => "NaN16", "Float16(-Inf)" => "-Inf16", "Float16(Inf)" => "Inf16")
elseif format === :midpoint
m = mid(a)
str_m = _round_string(m, sigdigits, RoundNearest)
str_m = _round_string(m, sigdigits)
# str_m = ifelse(m ≥ 0, string('+', str_m), str_m)
output = string("Float16(", str_m, ") ± Float16(", _round_string(radius(a), sigdigits, RoundUp), ')')
output = string("Float16(", str_m, ") ± Float16(", _round_string(radius(a), sigdigits), ')')
return replace(output, "Float16(NaN)" => "NaN16", "Float16(Inf)" => '∞')
else
str_lo = _round_string(lo, sigdigits, RoundDown)
str_lo = _round_string(lo, sigdigits)
# str_lo = ifelse(lo ≥ 0, string('+', str_lo), str_lo)
str_hi = _round_string(sup(a), sigdigits, RoundUp)
str_hi = _round_string(sup(a), sigdigits)
# str_hi = ifelse(hi ≥ 0, string('+', str_hi), str_hi)
output = string("[Float16(", str_lo, "), Float16(", str_hi, ")]")
return replace(output, "Float16(NaN)" => "NaN16", "[Float16(-Inf)" => "(-∞", "Float16(Inf)]" => "∞)")
Expand All @@ -308,102 +308,19 @@ function _str_basic_repr(a::BareInterval{<:Rational}, format::Symbol)
end
end

# round to the prescribed significant digits
# code inspired by `_string(x::BigFloat, k::Integer)` in base/mpfr.jl

function _round_string(x::T, sigdigits::Int, r::RoundingMode) where {T<:AbstractFloat}
str_x = string(x)
str_digits = split(contains(str_x, '.') ? split(str_x, '.'; limit = 2)[2] : str_x, 'e'; limit = 2)[1]
len = length(str_digits)
if isinteger(x) && sigdigits ≥ len # `x` is exactly representable
return replace(_round_string(big(x), length(str_x), RoundNearest), "e-0" => "e-")
elseif ispow2(abs(x)) && sigdigits ≥ len # `x` is exactly representable
return replace(_round_string(big(x), len + 1, RoundNearest), "e-0" => "e-")
else
return _round_string(big(x), sigdigits, r)
end
end
# truncate to the prescribed significant digits

_round_string(x::BigFloat, sigdigits::Int, ::RoundingMode{:Nearest}) =
Base.MPFR._string(x, sigdigits-1) # `sigdigits-1` digits after the decimal

function _round_string(x::BigFloat, sigdigits::Int, r::RoundingMode)
if !isfinite(x)
return string(Float64(x))
else
str_x = string(x)
str_digits = split(split(str_x, '.'; limit = 2)[2], 'e'; limit = 2)[1]
len = length(str_digits)
if isinteger(x) && sigdigits ≥ len # `x` is exactly representable
return _round_string(big(x), length(str_x), RoundNearest)
elseif ispow2(abs(x)) && sigdigits ≥ len # `x` is exactly representable
return _round_string(big(x), len + 1, RoundNearest)
else
# `sigdigits` digits after the decimal
str = Base.MPFR.string_mpfr(x, "%.$(sigdigits)Re")
rounded_str = _round_string(str, r)
return Base.MPFR._prettify_bigfloat(rounded_str)
end
end
function _round_string(x::AbstractFloat, sigdigits::Int)
!isfinite(x) && return string(x)
max_sig_digits = _count_sigdigits(string(x))
ndigits = min(sigdigits, max_sig_digits)
str = Printf.@sprintf("%.*g", ndigits, x)
occursin(r"[eE]", str) && return replace(str, r"^([+-]?\d+)(?=[eE])" => s"\1.0", r"([eE][+-])0+(\d+)" => s"\1\2")
!occursin(r"\.", str) && return str * ".0"
return str
end

_round_string(s::String, ::RoundingMode{:Up}) =
startswith(s, '-') ? string('-', _round_string_down(s[2:end])) : _round_string_up(s)

_round_string(s::String, ::RoundingMode{:Down}) =
startswith(s, '-') ? string('-', _round_string_up(s[2:end])) : _round_string_down(s)

function _round_string_up(s::String)
# `s` has one extra significant digit to control the rounding
mantissa, exponent = eachsplit(s, 'e')
mantissa = mantissa[1:end-1]
len = length(mantissa)
idx = findlast(d -> (d !== '9') & (d !== '.'), mantissa)
if idx == len # last significant digit is not `9`
d = parse(Int, mantissa[len]) + 1 # increase the last significant digit
return string(view(mantissa, 1:len-1), d, 'e', exponent)
else
if isnothing(idx) # all significant digits are `9`
expo = parse(Int, exponent) + 1 # increase the exponent by `1`
expo_str = string(expo; pad = 2)
exponent = expo < 0 ? expo_str : string('+', expo_str)
return string("1.", '0'^(len - 2), 'e', exponent)
else
new_mantissa = string(
view(mantissa, 1:idx-1),
parse(Int, mantissa[idx]) + 1,
# add `"."` if the last significant digit not equal to `9` is before the decimal point
idx == 1 ? "." : "",
'0'^(len - idx))
return string(new_mantissa, 'e', exponent)
end
end
end

function _round_string_down(s::String)
# `s` has one extra significant digit to control the rounding
mantissa, exponent = eachsplit(s, 'e')
len = length(mantissa)
idx = findlast(d -> (d !== '0') & (d !== '.'), mantissa)
if idx == len # last significant digit is not `0`
return string(view(mantissa, 1:len-1), 'e', exponent) # truncate
else
if isnothing(idx) # all significant digits are `0`
expo = parse(Int, exponent) - 1 # decrease the exponent by `1`
expo_str = string(expo; pad = 2)
exponent = expo < 0 ? expo_str : string('+', expo_str)
return string("9.", '9'^(len - 3), 'e', exponent)
else
new_mantissa = string(
view(mantissa, 1:idx-1),
parse(Int, mantissa[idx]) - 1,
# add `"."` if the last significant digit not equal to `0` is before the decimal point
idx == 1 ? "." : "",
'9'^(len - (idx + 1)))
return string(new_mantissa, 'e', exponent)
end
end
end
_count_sigdigits(s::AbstractString) = length(replace(split(s, r"[eE]")[1], '-' => "", '.' => "", r"^0+" => ""))

#

Expand Down
Loading
Loading