Skip to content

Commit 6dce3a9

Browse files
Kolarudpsanders
authored andcommitted
Fix mid for large floats (#147)
* Fix mid for large floats * Fix to comply with ITF 1788 tests, add doc and tests * Scale mid for unbounded intervals * Complete documentations and comments for edge cases. * Fix tests for bisect with infinite interval
1 parent 1114b4c commit 6dce3a9

File tree

3 files changed

+49
-18
lines changed

3 files changed

+49
-18
lines changed

src/intervals/arithmetic.jl

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -369,24 +369,41 @@ end
369369
"""
370370
mid(a::Interval, α=0.5)
371371
372-
Find the midpoint (or, in general, an intermediate point) at a distance α along the interval `a`. The default is the true midpoint at α=0.5.
372+
Find an intermediate point at a relative position `α`` in the interval `a`.
373+
The default is the true midpoint at `α = 0.5`.
373374
374375
Assumes 0 ≤ α ≤ 1.
376+
377+
Warning: if the parameter `α = 0.5` is explicitely set, the behavior differs
378+
from the default case if the provided `Interval` is not finite, since when
379+
`α` is provided `mid` simply replaces `+∞` (respectively `-∞`) by `prevfloat(+∞)`
380+
(respecively `nextfloat(-∞)`) for the computation of the intermediate point.
375381
"""
376382
function mid(a::Interval{T}, α) where T
377383

378384
isempty(a) && return convert(T, NaN)
379-
isentire(a) && return zero(a.lo)
380385

381-
a.lo == -&& return nextfloat(-∞)
382-
a.hi == +&& return prevfloat(+∞)
386+
lo = (a.lo == -? nextfloat(-) : a.lo)
387+
hi = (a.hi == +? prevfloat(+) : a.hi)
383388

384-
# @assert 0 ≤ α ≤ 1
385-
386-
# return (1-α) * a.lo + α * a.hi # rounds to nearest
387-
return α*(a.hi - a.lo) + a.lo # rounds to nearest
389+
midpoint = α * (hi - lo) + lo
390+
isfinite(midpoint) && return midpoint
391+
#= Fallback in case of overflow: hi - lo == +∞.
392+
This case can not be the default one as it does not pass several
393+
IEEE1788-2015 tests for small floats.
394+
=#
395+
return (1-α) * lo + α * hi
388396
end
389397

398+
"""
399+
mid(a::Interval)
400+
401+
Find the midpoint of interval `a`.
402+
403+
For intervals of the form `[-∞, x]` or `[x, +∞]` where `x` is finite, return
404+
respectively `nextfloat(-∞)` and `prevfloat(+∞)`. Note that it differs from the
405+
behavior of `mid(a, α=0.5)`.
406+
"""
390407
function mid(a::Interval{T}) where T
391408

392409
isempty(a) && return convert(T, NaN)
@@ -395,9 +412,13 @@ function mid(a::Interval{T}) where T
395412
a.lo == -&& return nextfloat(a.lo)
396413
a.hi == +&& return prevfloat(a.hi)
397414

398-
# @assert 0 ≤ α ≤ 1
399-
400-
return 0.5 * (a.lo + a.hi)
415+
midpoint = 0.5 * (a.lo + a.hi)
416+
isfinite(midpoint) && return midpoint
417+
#= Fallback in case of overflow: a.hi + a.lo == +∞ or a.hi + a.lo == -∞.
418+
This case can not be the default one as it does not pass several
419+
IEEE1788-2015 tests for small floats.
420+
=#
421+
return 0.5 * a.lo + 0.5 * a.hi
401422
end
402423

403424
mid(a::Interval{Rational{T}}) where T = (1//2) * (a.lo + a.hi)

test/interval_tests/bisect.jl

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@ using Base.Test
1111

1212
X = -..
1313
@test bisect(X, 0.5) == (-..0, 0..∞)
14-
@test bisect(X, 0.75) == (-..0, 0..∞)
15-
16-
X = 1..
17-
@test bisect(X) == (Interval(1, prevfloat(∞)), Interval(prevfloat(∞), ∞))
14+
B = bisect(X, 0.75)
15+
@test B[1].hi > 0
16+
@test B[1].hi == B[2].lo
17+
B = bisect(X, 0.25)
18+
@test B[1].hi < 0
19+
@test B[1].hi == B[2].lo
1820

1921
X = (0..1) × (0..2)
2022
@test bisect(X, 0.5) == ( (0..1) × (0..1), (0..1) × (1..2) )
@@ -26,5 +28,5 @@ using Base.Test
2628
IntervalBox(0..1, Interval(0.9921875, 2.0)))
2729

2830
X = (-..∞) × (-..∞)
29-
@test bisect(X) == ( (-..0) × (-..∞), (0..∞) × (-..∞))
31+
@test bisect(X, 0.5) == ( (-..0) × (-..∞), (0..∞) × (-..∞))
3032
end

test/interval_tests/consistency.jl

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,8 +195,16 @@ setprecision(Interval, Float64)
195195

196196
@testset "mid with parameter" begin
197197
@test mid(0..1, 0.75) == 0.75
198-
@test mid(1..∞, 0.75) == prevfloat(∞)
199-
@test mid(-..∞, 0.75) == 0
198+
@test mid(1..∞, 0.75) > 0
199+
@test mid(-..∞, 0.75) > 0
200+
@test mid(-..∞, 0.25) < 0
201+
end
202+
203+
@testset "mid with large floats" begin
204+
@test mid(0.8e308..1.2e308) == 1e308
205+
@test mid(-1e308..1e308) == 0
206+
@test isfinite(mid(0.8e308..1.2e308, 0.75))
207+
@test isfinite(mid(-1e308..1e308, 0.75))
200208
end
201209

202210
@testset "diam" begin

0 commit comments

Comments
 (0)