Skip to content

Commit

Permalink
(re)Introduce scaling factor for mid (#610)
Browse files Browse the repository at this point in the history
* Merge _relpoint into mid with an optional argument

* Make sure alpha is in [0, 1]

* Tests
  • Loading branch information
Kolaru authored Jan 15, 2024
1 parent 63e0481 commit 946493a
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 65 deletions.
37 changes: 1 addition & 36 deletions src/intervals/interval_operations/bisect.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Split the `i`-th component of a vector `x` at a relative position `α`, where
function bisect(x::BareInterval{T}, α::Real = 0.5) where {T<:NumTypes}
0 α 1 || return throw(DomainError(α, "`bisect` only accepts a relative position between 0 and 1"))
isatomic(x) && return (x, emptyinterval(BareInterval{T}))
m = _relpoint(x, α)
m = mid(x, α)
return (_unsafe_bareinterval(T, inf(x), m), _unsafe_bareinterval(T, m, sup(x)))
end

Expand All @@ -30,41 +30,6 @@ function bisect(x::AbstractVector, i::Integer, α::Real = 0.5)
return (x₁, x₂)
end

# helper functions for bisection

function _relpoint(x::BareInterval{T}, α::Real) where {T<:AbstractFloat}
α == 0.5 && return mid(x)
isempty_interval(x) && return convert(T, NaN)
if isentire_interval(x)
α > 0.5 && return prevfloat(typemax(T))
return nextfloat(typemin(T))
else
lo, hi = bounds(x)
lo == typemin(T) && return nextfloat(lo) # cf. Section 12.12.8
hi == typemax(T) && return prevfloat(hi) # cf. Section 12.12.8
β = convert(T, α)
midpoint = β * (hi - lo) + lo
isfinite(midpoint) && return _normalisezero(midpoint)
return _normalisezero((1 - β) * lo + β * hi)
end
end
function _relpoint(x::BareInterval{T}, α::Real) where {T<:Rational}
α == 0.5 && return mid(x)
isempty_interval(x) && return throw(ArgumentError("cannot compute the midpoint of empty intervals; cannot return a `Rational` NaN"))
if isentire_interval(x)
α > 0.5 && return prevfloat(typemax(T))
return nextfloat(typemin(T))
else
lo, hi = bounds(x)
lo == typemin(T) && return nextfloat(lo) # cf. Section 12.12.8
hi == typemax(T) && return prevfloat(hi) # cf. Section 12.12.8
β = convert(T, α)
midpoint = β * (hi - lo) + lo
isfinite(midpoint) && return _normalisezero(midpoint)
return _normalisezero((1 - β) * lo + β * hi)
end
end

"""
mince(x, n)
Expand Down
77 changes: 48 additions & 29 deletions src/intervals/interval_operations/numeric.jl
Original file line number Diff line number Diff line change
Expand Up @@ -84,43 +84,62 @@ Implement the `mid` function of the IEEE Standard 1788-2015 (Table 9.2).
See also: [`inf`](@ref), [`sup`](@ref), [`bounds`](@ref), [`diam`](@ref),
[`radius`](@ref) and [`midradius`](@ref).
mid(x, α)
Relative midpoint of `x`, for `α` between 0 and 1.
`mid(x, 0)` is the lower bound of the interval, `mid(x, 1)` the upper bound,
and `mid(x, 0.5)` the midpoint.
"""
function mid(x::BareInterval{T}) where {T<:AbstractFloat}
function mid(x::BareInterval{T}, α = 0.5) where {T<:AbstractFloat}
!(0 <= α <= 1) && throw(DomainError(α, "α must be between 0 and 1"))
isempty_interval(x) && return convert(T, NaN)
isentire_interval(x) && return zero(T)
lo, hi = bounds(x)
lo == typemin(T) && return nextfloat(lo) # cf. Section 12.12.8
hi == typemax(T) && return prevfloat(hi) # cf. Section 12.12.8
midpoint = (lo + hi) / 2
isfinite(midpoint) && return _normalisezero(midpoint)
# fallback in case of overflow
# cannot be the default, since it does not pass several IEEE 1788-2015 tests for small floats
return _normalisezero(lo / 2 + hi / 2)
end
function mid(x::BareInterval{T}) where {T<:Rational}
if isentire_interval(x)
α == 0.5 && return zero(T)
α > 0.5 && return prevfloat(typemax(T))
return nextfloat(typemin(T))
else
lo, hi = bounds(x)
lo == typemin(T) && return nextfloat(lo) # cf. Section 12.12.8
hi == typemax(T) && return prevfloat(hi) # cf. Section 12.12.8
β = convert(T, α)
midpoint = β * (hi + lo * (1/β - 1)) # Exactly 0.5 * (hi + lo) for β = 0.5
isfinite(midpoint) && return _normalisezero(midpoint)
return _normalisezero((1 - β) * lo + β * hi)
end
end
function mid(x::BareInterval{T}, α = 1//2) where {T<:Rational}
!(0 <= α <= 1) && throw(DomainError(α, "α must be between 0 and 1"))
isempty_interval(x) && return throw(ArgumentError("cannot compute the midpoint of empty intervals; cannot return a `Rational` NaN"))
isentire_interval(x) && return zero(T)
lo, hi = bounds(x)
lo == typemin(T) && return nextfloat(lo) # cf. Section 12.12.8
hi == typemax(T) && return prevfloat(hi) # cf. Section 12.12.8
midpoint = (lo + hi) / 2
isfinite(midpoint) && return _normalisezero(midpoint)
# fallback in case of overflow
# cannot be the default, since it does not pass several IEEE 1788-2015 tests for small floats
return _normalisezero(lo / 2 + hi / 2)
end

function mid(x::Interval{T}) where {T<:AbstractFloat}
if isentire_interval(x)
α == 0.5 && return zero(T)
α > 0.5 && return prevfloat(typemax(T))
return nextfloat(typemin(T))
else
lo, hi = bounds(x)
lo == typemin(T) && return nextfloat(lo) # cf. Section 12.12.8
hi == typemax(T) && return prevfloat(hi) # cf. Section 12.12.8
β = convert(T, α)
midpoint = β * (hi - lo) + lo
isfinite(midpoint) && return _normalisezero(midpoint)
return _normalisezero((1 - β) * lo + β * hi)
end
end
function mid(x::Interval{T}, α = 0.5) where {T<:AbstractFloat}
!(0 <= α <= 1) && throw(DomainError(α, "α must be between 0 and 1"))
isnai(x) && return convert(T, NaN)
return mid(bareinterval(x))
return mid(bareinterval(x), α)
end
function mid(x::Interval{<:Rational})
function mid(x::Interval{<:Rational}, α = 1//2)
!(0 <= α <= 1) && throw(DomainError(α, "α must be between 0 and 1"))
isnai(x) && return throw(ArgumentError("cannot compute the midpoint of an NaI; cannot return a `Rational` NaN"))
return mid(bareinterval(x))
return mid(bareinterval(x), α)
end

mid(x::Real) = mid(interval(x))
mid(x::Complex) = complex(mid(real(x)), mid(imag(x)))
mid(x::Real, α = 0.5) = mid(interval(x), α)
mid(x::Complex, α = 0.5) = complex(mid(real(x), α), mid(imag(x), α))

"""
diam(x)
Expand Down
10 changes: 10 additions & 0 deletions test/interval_tests/consistency.jl
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,16 @@
@test isnan(mid(emptyinterval()))
end

@testset "mid with α" begin
@test_throws DomainError mid(interval(1, 2), 1.2)
@test_throws DomainError mid(interval(1, 2), -0.7)
@test mid(interval(0, 1), 0.75) == 0.75
@test mid(interval(0, 1000), 0.125) == 125
@test mid(interval(1, Inf), 0.75) > 0
@test mid(interval(-Inf, Inf), 0.75) > 0
@test mid(interval(-Inf, Inf), 0.25) < 0
end

@testset "mid with large floats" begin
@test mid(interval(0.8e308, 1.2e308)) == 1e308
@test mid(interval(-1e308, 1e308)) == 0
Expand Down

0 comments on commit 946493a

Please sign in to comment.