Skip to content

Commit a8ccabf

Browse files
authored
Release 1.0 (#698)
1 parent 9c063f0 commit a8ccabf

File tree

12 files changed

+188
-289
lines changed

12 files changed

+188
-289
lines changed

.github/workflows/CI.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ jobs:
1111
fail-fast: false
1212
matrix:
1313
version:
14-
- '1.9'
14+
- '1.10'
1515
- '1'
1616
- 'nightly'
1717
os:

Project.toml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ CRlibm = "96374032-68de-5a5b-8d9e-752f78720389"
88
MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09"
99
OpenBLASConsistentFPCSR_jll = "6cdc7f73-28fd-5e50-80fb-958a8875b1af"
1010
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
11+
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
1112
RoundingEmulator = "5eaf0fd0-dfba-4ccb-bf02-d820a40db705"
1213

1314
[weakdeps]
@@ -32,13 +33,14 @@ IntervalArithmeticSparseArraysExt = "SparseArrays"
3233
Arblib = "1.3.0"
3334
CRlibm = "1.0.2"
3435
DiffRules = "1"
35-
ForwardDiff = "0.10, 1"
36+
ForwardDiff = "1"
3637
IntervalSets = "0.7"
37-
LinearAlgebra = "1.9"
38+
LinearAlgebra = "1.10"
3839
MacroTools = "0.5"
3940
OpenBLASConsistentFPCSR_jll = "0.3.29"
40-
Random = "1.9"
41+
Random = "1.10"
42+
Printf = "1.10"
4143
RecipesBase = "1"
4244
RoundingEmulator = "0.2"
43-
SparseArrays = "1.9.0"
44-
julia = "1.9"
45+
SparseArrays = "1.10"
46+
julia = "1.10"

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ The official documentation is available online: https://juliaintervals.github.io
1919

2020
## Installation
2121

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

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

docs/src/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ IntervalArithmetic.jl is a Julia package for validated numerics in Julia. All ca
77
## Installation
88

99
```@repl
10-
using Pkg # Julia v1.9 or above
10+
using Pkg # Julia v1.10 or above
1111
redirect_stderr(devnull) do # hide
1212
Pkg.add("IntervalArithmetic")
1313
end # hide

ext/IntervalArithmeticForwardDiffExt.jl

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@ Base.promote_rule(::Type{ExactReal{S}}, ::Type{Dual{T, V, N}}) where {S<:Real, T
2323
Base.promote_rule(::Type{Dual{T, V, N}}, ::Type{ExactReal{S}}) where {S<:Real, T, V, N} =
2424
Dual{T,ExactReal{IntervalArithmetic.promote_numtype(V, S)},N}
2525

26-
Base.:(==)(x::Union{BareInterval,Interval}, y::Dual) = x == value(y)
27-
Base.:(==)(x::Dual, y::Union{BareInterval,Interval}) = value(x) == y
26+
Base.:(==)(x::Interval, y::Dual) = x == value(y)
27+
Base.:(==)(x::Dual, y::Interval) = value(x) == y
28+
Base.:<(x::Interval, y::Dual) = x < value(y)
29+
Base.:<(x::Dual, y::Interval) = value(x) < y
2830

2931
function Base.:(^)(x::Dual{Txy,<:Interval}, y::Dual{Txy,<:Interval}) where {Txy}
3032
vx, vy = value(x), value(y)
@@ -91,10 +93,9 @@ function Base.:(^)(x::ExactReal, y::Dual{<:Ty}) where {Ty}
9193
end
9294
end
9395

94-
9596
# Piecewise functions
9697

97-
function (constant::Constant)(::Dual{T, Interval{S}}) where {T, S}
98+
function (constant::Constant)(::Dual{T,Interval{S}}) where {T, S}
9899
return Dual{T}(interval(S, constant.value), interval(S, 0.0))
99100
end
100101

src/IntervalArithmetic.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ include("piecewise.jl")
7777

7878
#
7979

80+
import Printf
81+
8082
include("display.jl")
8183
export setdisplay
8284

@@ -228,7 +230,7 @@ Configure the default behavior for:
228230
[`IntervalArithmetic.MatMulMode`](@ref).
229231
Keyword: `matmul`. Available options: `:fast` (default), `:slow`.
230232
"""
231-
function configure(; numtype::Type{<:NumTypes}=Float64, flavor::Symbol=:set_based, rounding::Symbol=:correct, power::Symbol=:fast, matmul::Symbol=:slow)
233+
function configure(; numtype::Type{<:NumTypes}=Float64, flavor::Symbol=:set_based, rounding::Symbol=:correct, power::Symbol=:fast, matmul::Symbol=:fast)
232234
configure_numtype(numtype)
233235
configure_flavor(flavor)
234236
configure_rounding(rounding)

src/display.jl

Lines changed: 25 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ Display options:
4949
- significant digits: 6
5050
5151
julia> x = interval(0.1, 0.3)
52-
[0.0999999, 0.300001]_com
52+
[0.1, 0.3]_com
5353
5454
julia> setdisplay(:full)
5555
Display options:
@@ -69,7 +69,7 @@ Display options:
6969
- significant digits: 3
7070
7171
julia> x
72-
[0.0999, 0.301]_com
72+
[0.1, 0.3]_com
7373
7474
julia> setdisplay(; decorations = false)
7575
Display options:
@@ -79,7 +79,7 @@ Display options:
7979
- significant digits: 3
8080
8181
julia> x
82-
[0.0999, 0.301]
82+
[0.1, 0.3]
8383
```
8484
"""
8585
function setdisplay(format::Symbol = display_options.format;
@@ -196,14 +196,14 @@ function _str_basic_repr(a::BareInterval{<:AbstractFloat}, format::Symbol)
196196
return string(str_lo, ", ", str_hi)
197197
elseif format === :midpoint
198198
m = mid(a)
199-
str_m = _round_string(m, sigdigits, RoundNearest)
199+
str_m = _round_string(m, sigdigits)
200200
# str_m = ifelse(m ≥ 0, string('+', str_m), str_m)
201-
output = string(str_m, " ± ", _round_string(radius(a), sigdigits, RoundUp))
201+
output = string(str_m, " ± ", _round_string(radius(a), sigdigits))
202202
return replace(output, "Inf" => '')
203203
else
204-
str_lo = _round_string(lo, sigdigits, RoundDown)
204+
str_lo = _round_string(lo, sigdigits)
205205
# str_lo = ifelse(lo ≥ 0, string('+', str_lo), str_lo)
206-
str_hi = _round_string(hi, sigdigits, RoundUp)
206+
str_hi = _round_string(hi, sigdigits)
207207
# str_hi = ifelse(hi ≥ 0, string('+', str_hi), str_hi)
208208
output = string('[', str_lo, ", ", str_hi, ']')
209209
return replace(output, "Inf]" => "∞)", "[-Inf" => "(-∞")
@@ -231,26 +231,26 @@ function _str_basic_repr(a::BareInterval{Float32}, format::Symbol)
231231
return string(str_lo, ", ", str_hi)
232232
elseif format === :midpoint
233233
m = mid(a)
234-
str_m = _round_string(m, sigdigits, RoundNearest)
234+
str_m = _round_string(m, sigdigits)
235235
str_m = replace(string(str_m, "f0"), "NaNf0" => "NaN32", "Inff0" => "Inf32")
236236
if contains(str_m, 'e')
237237
str_m = replace(str_m, 'e' => 'f', "f0" => "")
238238
end
239239
# str_m = ifelse(m ≥ 0, string('+', str_m), str_m)
240-
str_r = _round_string(radius(a), sigdigits, RoundUp)
240+
str_r = _round_string(radius(a), sigdigits)
241241
str_r = replace(string(str_r, "f0"), "NaNf0" => "NaN32", "Inff0" => "Inf32")
242242
if contains(str_r, 'e')
243243
str_r = replace(str_r, 'e' => 'f', "f0" => "")
244244
end
245245
return string(str_m, " ± ", str_r)
246246
else
247-
str_lo = _round_string(lo, sigdigits, RoundDown)
247+
str_lo = _round_string(lo, sigdigits)
248248
str_lo = replace(string('[', str_lo, "f0"), "NaNf0" => "NaN32", "[-Inff0" => "(-∞")
249249
if contains(str_lo, 'e')
250250
str_lo = replace(str_lo, 'e' => 'f', "f0" => "")
251251
end
252252
# str_lo = ifelse(lo ≥ 0, string('+', str_lo), str_lo)
253-
str_hi = _round_string(hi, sigdigits, RoundUp)
253+
str_hi = _round_string(hi, sigdigits)
254254
str_hi = replace(string(str_hi, "f0]"), "NaNf0" => "NaN32", "Inff0]" => "∞)")
255255
if contains(str_hi, 'e')
256256
str_hi = replace(str_hi, 'e' => 'f', "f0" => "")
@@ -274,14 +274,14 @@ function _str_basic_repr(a::BareInterval{Float16}, format::Symbol)
274274
return replace(output, "Float16(NaN)" => "NaN16", "Float16(-Inf)" => "-Inf16", "Float16(Inf)" => "Inf16")
275275
elseif format === :midpoint
276276
m = mid(a)
277-
str_m = _round_string(m, sigdigits, RoundNearest)
277+
str_m = _round_string(m, sigdigits)
278278
# str_m = ifelse(m ≥ 0, string('+', str_m), str_m)
279-
output = string("Float16(", str_m, ") ± Float16(", _round_string(radius(a), sigdigits, RoundUp), ')')
279+
output = string("Float16(", str_m, ") ± Float16(", _round_string(radius(a), sigdigits), ')')
280280
return replace(output, "Float16(NaN)" => "NaN16", "Float16(Inf)" => '')
281281
else
282-
str_lo = _round_string(lo, sigdigits, RoundDown)
282+
str_lo = _round_string(lo, sigdigits)
283283
# str_lo = ifelse(lo ≥ 0, string('+', str_lo), str_lo)
284-
str_hi = _round_string(sup(a), sigdigits, RoundUp)
284+
str_hi = _round_string(sup(a), sigdigits)
285285
# str_hi = ifelse(hi ≥ 0, string('+', str_hi), str_hi)
286286
output = string("[Float16(", str_lo, "), Float16(", str_hi, ")]")
287287
return replace(output, "Float16(NaN)" => "NaN16", "[Float16(-Inf)" => "(-∞", "Float16(Inf)]" => "∞)")
@@ -308,102 +308,19 @@ function _str_basic_repr(a::BareInterval{<:Rational}, format::Symbol)
308308
end
309309
end
310310

311-
# round to the prescribed significant digits
312-
# code inspired by `_string(x::BigFloat, k::Integer)` in base/mpfr.jl
313-
314-
function _round_string(x::T, sigdigits::Int, r::RoundingMode) where {T<:AbstractFloat}
315-
str_x = string(x)
316-
str_digits = split(contains(str_x, '.') ? split(str_x, '.'; limit = 2)[2] : str_x, 'e'; limit = 2)[1]
317-
len = length(str_digits)
318-
if isinteger(x) && sigdigits len # `x` is exactly representable
319-
return replace(_round_string(big(x), length(str_x), RoundNearest), "e-0" => "e-")
320-
elseif ispow2(abs(x)) && sigdigits len # `x` is exactly representable
321-
return replace(_round_string(big(x), len + 1, RoundNearest), "e-0" => "e-")
322-
else
323-
return _round_string(big(x), sigdigits, r)
324-
end
325-
end
311+
# truncate to the prescribed significant digits
326312

327-
_round_string(x::BigFloat, sigdigits::Int, ::RoundingMode{:Nearest}) =
328-
Base.MPFR._string(x, sigdigits-1) # `sigdigits-1` digits after the decimal
329-
330-
function _round_string(x::BigFloat, sigdigits::Int, r::RoundingMode)
331-
if !isfinite(x)
332-
return string(Float64(x))
333-
else
334-
str_x = string(x)
335-
str_digits = split(split(str_x, '.'; limit = 2)[2], 'e'; limit = 2)[1]
336-
len = length(str_digits)
337-
if isinteger(x) && sigdigits len # `x` is exactly representable
338-
return _round_string(big(x), length(str_x), RoundNearest)
339-
elseif ispow2(abs(x)) && sigdigits len # `x` is exactly representable
340-
return _round_string(big(x), len + 1, RoundNearest)
341-
else
342-
# `sigdigits` digits after the decimal
343-
str = Base.MPFR.string_mpfr(x, "%.$(sigdigits)Re")
344-
rounded_str = _round_string(str, r)
345-
return Base.MPFR._prettify_bigfloat(rounded_str)
346-
end
347-
end
313+
function _round_string(x::AbstractFloat, sigdigits::Int)
314+
!isfinite(x) && return string(x)
315+
max_sig_digits = _count_sigdigits(string(x))
316+
ndigits = min(sigdigits, max_sig_digits)
317+
str = Printf.@sprintf("%.*g", ndigits, x)
318+
occursin(r"[eE]", str) && return replace(str, r"^([+-]?\d+)(?=[eE])" => s"\1.0", r"([eE][+-])0+(\d+)" => s"\1\2")
319+
!occursin(r"\.", str) && return str * ".0"
320+
return str
348321
end
349322

350-
_round_string(s::String, ::RoundingMode{:Up}) =
351-
startswith(s, '-') ? string('-', _round_string_down(s[2:end])) : _round_string_up(s)
352-
353-
_round_string(s::String, ::RoundingMode{:Down}) =
354-
startswith(s, '-') ? string('-', _round_string_up(s[2:end])) : _round_string_down(s)
355-
356-
function _round_string_up(s::String)
357-
# `s` has one extra significant digit to control the rounding
358-
mantissa, exponent = eachsplit(s, 'e')
359-
mantissa = mantissa[1:end-1]
360-
len = length(mantissa)
361-
idx = findlast(d -> (d !== '9') & (d !== '.'), mantissa)
362-
if idx == len # last significant digit is not `9`
363-
d = parse(Int, mantissa[len]) + 1 # increase the last significant digit
364-
return string(view(mantissa, 1:len-1), d, 'e', exponent)
365-
else
366-
if isnothing(idx) # all significant digits are `9`
367-
expo = parse(Int, exponent) + 1 # increase the exponent by `1`
368-
expo_str = string(expo; pad = 2)
369-
exponent = expo < 0 ? expo_str : string('+', expo_str)
370-
return string("1.", '0'^(len - 2), 'e', exponent)
371-
else
372-
new_mantissa = string(
373-
view(mantissa, 1:idx-1),
374-
parse(Int, mantissa[idx]) + 1,
375-
# add `"."` if the last significant digit not equal to `9` is before the decimal point
376-
idx == 1 ? "." : "",
377-
'0'^(len - idx))
378-
return string(new_mantissa, 'e', exponent)
379-
end
380-
end
381-
end
382-
383-
function _round_string_down(s::String)
384-
# `s` has one extra significant digit to control the rounding
385-
mantissa, exponent = eachsplit(s, 'e')
386-
len = length(mantissa)
387-
idx = findlast(d -> (d !== '0') & (d !== '.'), mantissa)
388-
if idx == len # last significant digit is not `0`
389-
return string(view(mantissa, 1:len-1), 'e', exponent) # truncate
390-
else
391-
if isnothing(idx) # all significant digits are `0`
392-
expo = parse(Int, exponent) - 1 # decrease the exponent by `1`
393-
expo_str = string(expo; pad = 2)
394-
exponent = expo < 0 ? expo_str : string('+', expo_str)
395-
return string("9.", '9'^(len - 3), 'e', exponent)
396-
else
397-
new_mantissa = string(
398-
view(mantissa, 1:idx-1),
399-
parse(Int, mantissa[idx]) - 1,
400-
# add `"."` if the last significant digit not equal to `0` is before the decimal point
401-
idx == 1 ? "." : "",
402-
'9'^(len - (idx + 1)))
403-
return string(new_mantissa, 'e', exponent)
404-
end
405-
end
406-
end
323+
_count_sigdigits(s::AbstractString) = length(replace(split(s, r"[eE]")[1], '-' => "", '.' => "", r"^0+" => ""))
407324

408325
#
409326

0 commit comments

Comments
 (0)