diff --git a/base/range.jl b/base/range.jl index 0254718af8d82..8bb03152dc639 100644 --- a/base/range.jl +++ b/base/range.jl @@ -135,7 +135,16 @@ colon{T}(start::T, step, stop::T) = StepRange(start, step, stop) Construct a range by length, given a starting value and optional step (defaults to 1). """ -range{T,S}(a::T, step::S, len::Integer) = StepRange{T,S}(a, step, convert(T, a+step*(len-1))) +function range{T,S}(a::T, step::S, len::Integer) + r = StepRange{T,S}(a, step, convert(T, a+step*(len-1))) + if length(r) < len + r = StepRange{T,S}(a, step, convert(T, a+step*len)) + if length(r) > len + throw(InexactError()) + end + end + r +end ## floating point ranges @@ -402,10 +411,14 @@ step{T}(r::LinSpace{T}) = ifelse(r.len <= 0, convert(T,NaN), (r.stop-r.start)/r. unsafe_length(r::Range) = length(r) # generic fallback -function unsafe_length(r::StepRange) +function unsafe_length{T<:Integer,S<:Integer}(r::StepRange{T,S}) n = Integer(div(r.stop+r.step - r.start, r.step)) isempty(r) ? zero(n) : n end +function unsafe_length(r::StepRange) + n = round(Integer, (r.stop - r.start)/r.step + 1) + isempty(r) ? zero(n) : n +end length(r::StepRange) = unsafe_length(r) unsafe_length(r::AbstractUnitRange) = Integer(last(r) - first(r) + 1) unsafe_length(r::OneTo) = r.stop diff --git a/test/ranges.jl b/test/ranges.jl index 7d50aa93570d4..58f9608ccf086 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -782,3 +782,32 @@ io = IOBuffer() show(io, r) str = takebuf_string(io) @test str == "Base.OneTo(3)" + +# StepRange with roundoff error +# StepRange can't be constructed with AbstractFloat, so let's make a numeric type that +# acts like an AbstractFloat but won't be recognized as such +immutable FloatLike + val::Float64 +end +Base.zero(x::FloatLike) = FloatLike(0) +Base.isless(x::FloatLike, y::FloatLike) = isless(x.val, y.val) +Base.:+(x::FloatLike, y::FloatLike) = FloatLike(x.val+y.val) +Base.:-(x::FloatLike, y::FloatLike) = FloatLike(x.val-y.val) +Base.:/(x::FloatLike, y::FloatLike) = x.val / y.val +Base.rem(x::FloatLike, y::FloatLike) = FloatLike(rem(x.val, y.val)) +Base.:*(x::FloatLike, y::Int) = FloatLike(x.val * y) +Base.:/(x::FloatLike, y::Int) = FloatLike(x.val / y) + +rf = 0.8:0.8:640.0 +rs = StepRange(FloatLike(0.8), FloatLike(0.8), FloatLike(640)) +@test first(rs) == FloatLike(0.8) +@test last(rs).val ≈ 640 +@test length(rf) == length(rs) == 800 +rf = 640.0:-0.8:0.8 +rs = StepRange(FloatLike(640), FloatLike(-0.8), FloatLike(0.8)) +@test first(rs) == FloatLike(640) +@test last(rs).val ≈ 0.8 +@test length(rf) == length(rs) == 800 +rs = range(FloatLike(0), FloatLike(0.1), 10) +@test length(rs) == 10 +@test last(rs).val ≈ 0.9