Skip to content

Commit 373b19b

Browse files
tkoolendpsanders
authored andcommitted
Introduce closure function, extracted out of convert (#114)
* Introduce `closure` function, separate from convert. * Fix find_quadrants (implicit convert -> explicit closure). * Add docstring to closure. * Remove tests with undesired behavior. * Remove one convert method, test another. * closure -> atomic
1 parent e6375b0 commit 373b19b

File tree

9 files changed

+97
-56
lines changed

9 files changed

+97
-56
lines changed

src/decorations/intervals.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ convert(::Type{DecoratedInterval{T}}, x::S) where {T<:Real, S<:Integer} =
112112
# end
113113
function convert(::Type{DecoratedInterval{T}}, xx::DecoratedInterval) where T<:Real
114114
x = interval_part(xx)
115-
x = convert(Interval{T},x)
115+
x = atomic(Interval{T},x)
116116
DecoratedInterval( x, decoration(xx) )
117117
end
118118

src/intervals/conversion.jl

Lines changed: 73 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,77 @@ promote_rule(::Type{BigFloat}, ::Type{Interval{T}}) where T<:Real =
1212
Interval{promote_type(T, BigFloat)}
1313

1414

15+
# convert methods:
16+
convert(::Type{Interval{T}}, x) where {T} = atomic(Interval{T}, x)
17+
convert(::Type{Interval{T}}, x::T) where {T} = Interval{T}(x)
18+
convert(::Type{Interval{T}}, x::Interval{T}) where {T} = x
19+
convert(::Type{Interval{T}}, x::Interval) where {T} = atomic(Interval{T}, x)
1520

16-
# Floating point intervals:
21+
convert(::Type{Interval}, x::Real) = (T = typeof(float(x)); convert(Interval{T}, x))
22+
convert(::Type{Interval}, x::Interval) = x
1723

18-
convert(::Type{Interval{T}}, x::AbstractString) where T<:AbstractFloat =
19-
parse(Interval{T}, x)
24+
"""
25+
atomic(::Type{<:Interval}, x)
26+
27+
Construct the tightest interval of a given type that contains the value `x`.
28+
29+
If `x` is an `AbstractString`, the interval will be created by calling `parse`.
30+
31+
# Examples
32+
33+
Construct an `Interval{Float64}` containing a given `BigFloat`:
34+
35+
```julia
36+
julia> x = big"0.1"
37+
1.000000000000000000000000000000000000000000000000000000000000000000000000000002e-01
38+
39+
julia> i = IntervalArithmetic.atomic(Interval{Float64}, x)
40+
[0.0999999, 0.100001]
41+
42+
julia> i isa Interval{Float64}
43+
true
44+
45+
julia> i.lo <= x <= i.hi
46+
true
47+
48+
julia> i.hi === nextfloat(i.lo)
49+
true
50+
```
51+
52+
Construct an `Interval{Float32}` containing a the real number 0.1 in two ways:
53+
54+
```julia
55+
julia> i1 = IntervalArithmetic.atomic(Interval{Float32}, "0.1")
56+
[0.0999999, 0.100001]
2057
21-
convert(::Type{Interval}, x::AbstractString) = convert(Interval{Float64}, x)
58+
julia> i2 = IntervalArithmetic.atomic(Interval{Float32}, 1//10)
59+
[0.0999999, 0.100001]
2260
23-
function convert(::Type{Interval{T}}, x::S) where {T<:AbstractFloat, S<:Real}
61+
julia> i1 === i2
62+
true
63+
64+
julia> i.lo <= 1//10 <= i.hi
65+
true
66+
```
67+
"""
68+
function atomic end
69+
70+
# Integer intervals:
71+
atomic(::Type{Interval{T}}, x::T) where {T<:Integer} = Interval{T}(x)
72+
73+
# Floating point intervals:
74+
atomic(::Type{Interval{T}}, x::AbstractString) where T<:AbstractFloat =
75+
parse(Interval{T}, x)
76+
77+
function atomic(::Type{Interval{T}}, x::S) where {T<:AbstractFloat, S<:Real}
2478
isinf(x) && return wideinterval(T(x))
2579

2680
Interval{T}( T(x, RoundDown), T(x, RoundUp) )
2781
# the rounding up could be done as nextfloat of the rounded down one?
2882
# use @round_up and @round_down here?
2983
end
3084

31-
function convert(::Type{Interval{T}}, x::S) where {T<:AbstractFloat, S<:AbstractFloat}
85+
function atomic(::Type{Interval{T}}, x::S) where {T<:AbstractFloat, S<:AbstractFloat}
3286
isinf(x) && return wideinterval(x)#Interval{T}(prevfloat(T(x)), nextfloat(T(x)))
3387
# isinf(x) && return Interval{T}(prevfloat(x), nextfloat(x))
3488

@@ -41,50 +95,37 @@ function convert(::Type{Interval{T}}, x::S) where {T<:AbstractFloat, S<:Abstract
4195
return Interval(parse(T, xstr, RoundDown), parse(T, xstr, RoundUp))
4296
end
4397

44-
return convert(Interval{T}, xrat)
98+
return atomic(Interval{T}, xrat)
4599
end
46100

47-
convert(::Type{Interval{T}}, x::Interval{T}) where T<:AbstractFloat = x
48-
49-
function convert(::Type{Interval{T}}, x::Interval) where T<:AbstractFloat
101+
function atomic(::Type{Interval{T}}, x::Interval) where T<:AbstractFloat
50102
Interval{T}( T(x.lo, RoundDown), T(x.hi, RoundUp) )
51103
end
52104

53-
54105
# Complex numbers:
55-
convert(::Type{Interval{T}}, x::Complex{Bool}) where T<:AbstractFloat =
106+
atomic(::Type{Interval{T}}, x::Complex{Bool}) where T<:AbstractFloat =
56107
(x == im) ? one(T)*im : throw(ArgumentError("Complex{Bool} not equal to im"))
57108

58109

59110
# Rational intervals
60-
function convert(::Type{Interval{Rational{Int}}}, x::Irrational)
61-
a = float(convert(Interval{BigFloat}, x))
62-
convert(Interval{Rational{Int}}, a)
111+
function atomic(::Type{Interval{Rational{Int}}}, x::Irrational)
112+
a = float(atomic(Interval{BigFloat}, x))
113+
atomic(Interval{Rational{Int}}, a)
63114
end
64115

65-
function convert(::Type{Interval{Rational{BigInt}}}, x::Irrational)
66-
a = convert(Interval{BigFloat}, x)
67-
convert(Interval{Rational{BigInt}}, a)
116+
function atomic(::Type{Interval{Rational{BigInt}}}, x::Irrational)
117+
a = atomic(Interval{BigFloat}, x)
118+
atomic(Interval{Rational{BigInt}}, a)
68119
end
69120

70-
convert(::Type{Interval{Rational{T}}}, x::S) where {T<:Integer, S<:Integer} =
121+
atomic(::Type{Interval{Rational{T}}}, x::S) where {T<:Integer, S<:Integer} =
71122
Interval(x*one(Rational{T}))
72123

73-
convert(::Type{Interval{Rational{T}}}, x::Rational{S}) where
124+
atomic(::Type{Interval{Rational{T}}}, x::Rational{S}) where
74125
{T<:Integer, S<:Integer} = Interval(x*one(Rational{T}))
75126

76-
convert(::Type{Interval{Rational{T}}}, x::S) where {T<:Integer, S<:Float64} =
127+
atomic(::Type{Interval{Rational{T}}}, x::S) where {T<:Integer, S<:Float64} =
77128
Interval(rationalize(T, x))
78129

79-
convert(::Type{Interval{Rational{T}}}, x::S) where {T<:Integer, S<:BigFloat} =
130+
atomic(::Type{Interval{Rational{T}}}, x::S) where {T<:Integer, S<:BigFloat} =
80131
Interval(rationalize(T, x))
81-
82-
83-
# conversion to Interval without explicit type:
84-
function convert(::Type{Interval}, x::Real)
85-
T = typeof(float(x))
86-
87-
return convert(Interval{T}, x)
88-
end
89-
90-
convert(::Type{Interval}, x::Interval) = x

src/intervals/functions.jl

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
# Write explicitly like this to avoid ambiguity warnings:
99
for T in (:Integer, :Rational, :Float64, :BigFloat, :Interval)
10-
@eval ^(a::Interval{Float64}, x::$T) = convert(Interval{Float64}, big53(a)^x)
10+
@eval ^(a::Interval{Float64}, x::$T) = atomic(Interval{Float64}, big53(a)^x)
1111
end
1212

1313

@@ -98,7 +98,7 @@ function ^(a::Interval{BigFloat}, x::BigFloat)
9898
a = a domain
9999
(isempty(x) || isempty(a)) && return emptyinterval(a)
100100

101-
xx = convert(Interval{BigFloat}, x)
101+
xx = atomic(Interval{BigFloat}, x)
102102

103103
# @round() can't be used directly, because both arguments may
104104
# Inf or -Inf, which throws an error
@@ -135,7 +135,7 @@ end
135135
function ^(a::Interval{Rational{T}}, x::AbstractFloat) where T<:Integer
136136
a = Interval(a.lo.num/a.lo.den, a.hi.num/a.hi.den)
137137
a = a^x
138-
convert(Interval{Rational{T}}, a)
138+
atomic(Interval{Rational{T}}, a)
139139
end
140140

141141
# Rational power
@@ -149,13 +149,13 @@ function ^(a::Interval{BigFloat}, r::Rational{S}) where S<:Integer
149149
return emptyinterval(a)
150150
end
151151

152-
isinteger(r) && return convert(Interval{T}, a^round(S,r))
152+
isinteger(r) && return atomic(Interval{T}, a^round(S,r))
153153
r == one(S)//2 && return sqrt(a)
154154

155155
a = a domain
156156
(isempty(r) || isempty(a)) && return emptyinterval(a)
157157

158-
y = convert(Interval{BigFloat}, r)
158+
y = atomic(Interval{BigFloat}, r)
159159

160160
a^y
161161
end
@@ -245,7 +245,7 @@ for f in (:exp2, :exp10)
245245
end
246246
end
247247

248-
@eval ($f)(a::Interval{Float64}) = convert(Interval{Float64}, $f(big53(a))) # no CRlibm version
248+
@eval ($f)(a::Interval{Float64}) = atomic(Interval{Float64}, $f(big53(a))) # no CRlibm version
249249

250250
@eval function ($f)(a::Interval{BigFloat})
251251
isempty(a) && return a

src/intervals/hyperbolic.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,6 @@ for f in (:tanh, :asinh, :acosh, :atanh)
6464
@eval function ($f)(a::Interval{Float64})
6565
isempty(a) && return a
6666

67-
convert(Interval{Float64}, ($f)(big53(a)) )
67+
atomic(Interval{Float64}, ($f)(big53(a)) )
6868
end
6969
end

src/intervals/intervals.jl

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ Interval(x::Complex) = Interval(real(x)) + im*Interval(imag(x))
5656

5757
Interval{T}(x) where T = Interval(convert(T, x))
5858

59-
Interval{T}(x::Interval) where T = convert(Interval{T}, x)
59+
Interval{T}(x::Interval) where T = atomic(Interval{T}, x)
6060

6161
"""
6262
is_valid_interval(a::Real, b::Real)
@@ -122,7 +122,9 @@ include("hyperbolic.jl")
122122

123123
# Syntax for intervals
124124

125-
a..b = interval(convert(Interval, a).lo, convert(Interval, b).hi)
125+
function ..(a::T, b::S) where {T, S}
126+
interval(atomic(Interval{T}, a).lo, atomic(Interval{S}, b).hi)
127+
end
126128

127129
# ..(a::Integer, b::Integer) = interval(a, b)
128130
# ..(a::Integer, b::Real) = interval(a, nextfloat(float(b)))
@@ -135,3 +137,4 @@ macro I_str(ex) # I"[3,4]"
135137
end
136138

137139
a ± b = (a-b)..(a+b)
140+
±(a::Interval, b) = (a.lo - b)..(a.hi + b)

src/intervals/macros.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,13 +81,13 @@ by calling `transform`."""
8181
function make_interval(T, expr1, expr2)
8282
# @show expr1, expr2
8383

84-
expr1 = transform(expr1, :convert, :(Interval{$T}))
84+
expr1 = transform(expr1, :atomic, :(Interval{$T}))
8585

8686
if isempty(expr2) # only one argument
8787
return :(Interval($expr1))
8888
end
8989

90-
expr2 = transform(expr2[1], :convert, :(Interval{$T}))
90+
expr2 = transform(expr2[1], :atomic, :(Interval{$T}))
9191

9292
:(interval(($expr1).lo, ($expr2).hi))
9393
end

src/intervals/precision.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const parameters = IntervalParameters()
1818
doc"`big53` creates an equivalent `BigFloat` interval to a given `Float64` interval."
1919
function big53(a::Interval{Float64})
2020
setprecision(Interval, 53) do # precision of Float64
21-
convert(Interval{BigFloat}, a)
21+
atomic(Interval{BigFloat}, a)
2222
end
2323
end
2424

@@ -41,7 +41,7 @@ function setprecision(::Type{Interval}, ::Type{T}, prec::Integer) where T<:Abstr
4141

4242
parameters.precision_type = T
4343
parameters.precision = prec
44-
parameters.pi = convert(Interval{BigFloat}, pi)
44+
parameters.pi = atomic(Interval{BigFloat}, pi)
4545

4646
prec
4747
end
@@ -69,7 +69,7 @@ setprecision(::Type{Interval}, t::Tuple) = setprecision(Interval, t...)
6969
precision(::Type{Interval}) = (parameters.precision_type, parameters.precision)
7070

7171

72-
const float_interval_pi = convert(Interval{Float64}, pi) # does not change
72+
const float_interval_pi = atomic(Interval{Float64}, pi) # does not change
7373

7474
pi_interval(::Type{BigFloat}) = parameters.pi
7575
pi_interval(::Type{Float64}) = float_interval_pi
@@ -82,6 +82,6 @@ end
8282

8383

8484

85-
float(x::Interval{T}) where T = convert( Interval{float(T)}, x) # https://github.com/dpsanders/IntervalArithmetic.jl/issues/174
85+
float(x::Interval{T}) where T = atomic( Interval{float(T)}, x) # https://github.com/dpsanders/IntervalArithmetic.jl/issues/174
8686

87-
big(x::Interval) = convert(Interval{BigFloat}, x)
87+
big(x::Interval) = atomic(Interval{BigFloat}, x)

src/intervals/trigonometric.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ This is a rather indirect way to determine if π/2 and 3π/2 are contained
1919
in the interval; cf. the formula for sine of an interval in
2020
Tucker, *Validated Numerics*."""
2121

22-
function find_quadrants(x::AbstractFloat)
23-
temp = x / half_pi(x)
22+
function find_quadrants(x::T) where {T<:AbstractFloat}
23+
temp = atomic(Interval{T}, x) / half_pi(x)
2424
(floor(temp.lo), floor(temp.hi))
2525
end
2626

@@ -172,7 +172,7 @@ end
172172
function atan2(y::Interval{Float64}, x::Interval{Float64})
173173
(isempty(y) || isempty(x)) && return emptyinterval(Float64)
174174

175-
convert(Interval{Float64}, atan2(big53(y), big53(x)))
175+
atomic(Interval{Float64}, atan2(big53(y), big53(x)))
176176
end
177177

178178

test/interval_tests/construction.jl

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,17 +64,14 @@ using Base.Test
6464
@test convert(Interval, eu) == @interval(eu)
6565
@test convert(Interval, BigInt(1)) == Interval(BigInt(1))
6666
@test convert(Interval, 1//10) == @interval(1//10)
67-
@test convert(Interval, 0.1) == Interval(0.09999999999999999, 0.1)
68-
@test convert(Interval, big"0.1") == big"0.1"..big"0.1"
67+
@test convert(Interval, Interval(0.1, 0.2)) === Interval(0.1, 0.2)
6968

7069
@test convert(Interval{Rational{Int}}, 0.1) == Interval(1//10)
7170
# @test convert(Interval{Rational{BigInt}}, pi) == Interval{Rational{BigInt}}(pi)
7271

7372
## promotion
7473
@test promote(Interval(2//1,3//1), Interval(1, 2)) ==
7574
(Interval(2.0,3.0), Interval(1.0,2.0))
76-
@test promote(Interval(1.5), parse(BigFloat, "2.1")) ==
77-
(Interval(BigFloat(1.5)), big"2.1"..big"2.1")
7875
@test promote(Interval(1.0), pi) == (Interval(1.0), @interval(pi))
7976

8077
# Constructors from the macros @interval, @floatinterval @biginterval

0 commit comments

Comments
 (0)