Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace isfinite check in ranges with lo ≤ x ≤ hi #45646

Merged
merged 10 commits into from
Oct 3, 2022
15 changes: 7 additions & 8 deletions base/range.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1396,14 +1396,13 @@ function sum(r::AbstractRange{<:Real})
end

function _in_range(x, r::AbstractRange)
if !isfinite(x)
return false
elseif iszero(step(r))
return !isempty(r) && first(r) == x
else
n = round(Integer, (x - first(r)) / step(r)) + 1
return n >= 1 && n <= length(r) && r[n] == x
end
isempty(r) && return false
f, l = first(r), last(r)
# check for NaN, Inf, and large x that may overflow in the next calculation
f <= x <= l || l <= x <= f || return false
N5N3 marked this conversation as resolved.
Show resolved Hide resolved
iszero(step(r)) && return true
n = round(Integer, (x - f) / step(r)) + 1
n >= 1 && n <= length(r) && r[n] == x
end
in(x::Real, r::AbstractRange{<:Real}) = _in_range(x, r)
# This method needs to be defined separately since -(::T, ::T) can be implemented
Expand Down
33 changes: 30 additions & 3 deletions test/ranges.jl
Original file line number Diff line number Diff line change
Expand Up @@ -518,8 +518,10 @@ end
@test !(3.5 in 1:5)
@test (3 in 1:5)
@test (3 in 5:-1:1)
#@test (3 in 3+0*(1:5))
#@test !(4 in 3+0*(1:5))
@test (3 in 3 .+ 0*(1:5))
@test !(4 in 3 .+ 0*(1:5))
@test 0. in (0. .* (1:10))
@test !(0.1 in (0. .* (1:10)))

let r = 0.0:0.01:1.0
@test (r[30] in r)
Expand All @@ -536,8 +538,17 @@ end
x = (NaN16, Inf32, -Inf64, 1//0, -1//0)
@test !(x in r)
end

@test 1e40 ∉ 0:1.0 # Issue #45747
@test 1e20 ∉ 0:1e-20:1e-20
@test 1e20 ∉ 0:1e-20
@test 1.0 ∉ 0:1e-20:1e-20
@test 0.5 ∉ 0:1e-20:1e-20
@test 1 ∉ 0:1e-20:1e-20

@test_broken 17.0 ∈ 0:1e40 # Don't support really long ranges
end
@testset "in() works across types, including non-numeric types (#21728)" begin
@testset "in() works across types, including non-numeric types (#21728 and #45646)" begin
@test 1//1 in 1:3
@test 1//1 in 1.0:3.0
@test !(5//1 in 1:3)
Expand All @@ -558,6 +569,22 @@ end
@test !(Complex(1, 0) in Date(2017, 01, 01):Dates.Day(1):Date(2017, 01, 05))
@test !(π in Date(2017, 01, 01):Dates.Day(1):Date(2017, 01, 05))
@test !("a" in Date(2017, 01, 01):Dates.Day(1):Date(2017, 01, 05))

# We use Ducks because of their propensity to stand in a row and because we know
# that no additional methods (e.g. isfinite) are defined specifically for Ducks.
struct Duck
location::Int
end
Base.:+(x::Duck, y::Int) = Duck(x.location + y)
Base.:-(x::Duck, y::Int) = Duck(x.location - y)
Base.:-(x::Duck, y::Duck) = x.location - y.location
Base.isless(x::Duck, y::Duck) = isless(x.location, y.location)

@test Duck(3) ∈ Duck(1):2:Duck(5)
@test Duck(3) ∈ Duck(5):-2:Duck(2)
@test Duck(4) ∉ Duck(5):-2:Duck(1)
@test Duck(4) ∈ Duck(1):Duck(5)
@test Duck(0) ∉ Duck(1):Duck(5)
end
end
@testset "indexing range with empty range (#4309)" begin
Expand Down