From 0cc35585d23bf117d737c4891d44bde304501869 Mon Sep 17 00:00:00 2001 From: Stefan Karpinski Date: Wed, 1 Feb 2017 17:37:02 -0500 Subject: [PATCH 1/2] Fix computation of range length in certain cases (fix 0.5/#20373) This is the 0.5 edition: with this change a, b, n are always hit exactly by a:s:a+n*s construction tests. There are two parts to the fix: 1) check that the lifted rational endpoint is exact even *after* we've reduced terms of the fractions; 2) apply the same fix that I've proposed for the length in the non-lifted case. Combined backport of these commits: - e849169bcf8ef79bd7c1dbfb6200a5a218496ceb - b7ad743fa3a71d0724b3abaa8bded2122346d748 range tests: allow any range length that hits stop (#20532) (cherry picked from commit b7ad743fa3a71d0724b3abaa8bded2122346d748) --- base/range.jl | 7 +++++-- test/ranges.jl | 23 +++++++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/base/range.jl b/base/range.jl index 00b2b217c1f6a..86c5c826ecbeb 100644 --- a/base/range.jl +++ b/base/range.jl @@ -169,13 +169,16 @@ function colon{T<:AbstractFloat}(start::T, step::T, stop::T) a *= div(e,b) c *= div(e,d) eT = convert(T,e) - if (a+n*c)/eT == stop + if a/eT == start && (a+n*c)/eT == stop return FloatRange{T}(a, c, n+1, eT) end end end end - FloatRange{T}(start, step, floor(r)+1, one(step)) + len = max(0, floor(r) + 1) + stop′ = start + len*step + len += (start < stop′ <= stop) + (start > stop′ >= stop) + FloatRange{T}(start, step, len, one(step)) end colon{T<:AbstractFloat}(a::T, b::T) = colon(a, one(a), b) diff --git a/test/ranges.jl b/test/ranges.jl index b22db9b77a1b4..d5ffcbafd3b5b 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -326,6 +326,29 @@ for T = (Float32, Float64,),# BigFloat), @test [r[n:-2:1];] == [r;][n:-2:1] end +# issue #20373 (unliftable ranges with exact end points) +@test [3*0.05:0.05:0.2;] == [linspace(3*0.05,0.2,2);] == [3*0.05,0.2] +@test [0.2:-0.05:3*0.05;] == [linspace(0.2,3*0.05,2);] == [0.2,3*0.05] +@test [-3*0.05:-0.05:-0.2;] == [linspace(-3*0.05,-0.2,2);] == [-3*0.05,-0.2] +@test [-0.2:0.05:-3*0.05;] == [linspace(-0.2,-3*0.05,2);] == [-0.2,-3*0.05] + +for T = (Float32, Float64,), i = 1:2^15, n = 1:5 + start, step = randn(T), randn(T) + step == 0 && continue + stop = start + (n-1)*step + lo = hi = n + while start + (lo-1)*step == stop; lo -= 1; end + while start + (hi-1)*step == stop; hi += 1; end + r = start:step:stop + @test lo < length(r) < hi + @test start == first(r) + @test stop == last(r) + l = linspace(start,stop,n) + @test n == length(l) + @test_skip start == first(l) + @test_skip stop == last(l) +end + # linspace & ranges with very small endpoints for T = (Float32, Float64) z = zero(T) From 03f5df7693def3ff32fcf099cae604e1e49cda78 Mon Sep 17 00:00:00 2001 From: Stefan Karpinski Date: Mon, 13 Feb 2017 14:03:06 -0500 Subject: [PATCH 2/2] float ranges: for ambiguous length, use round((stop-start)/step)+1 Combined backport of these commits: - 2065842b27e499543c753557bb97eaa2c742ee7e - 9750b201a5f8618c2705c7044a06d4afeaa78377 - 35a0bf495869a534e7050fcc50f2b2088f2b972a --- base/range.jl | 13 ++++++++++--- test/ranges.jl | 6 +++++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/base/range.jl b/base/range.jl index 86c5c826ecbeb..e2c8d0b00defb 100644 --- a/base/range.jl +++ b/base/range.jl @@ -175,9 +175,16 @@ function colon{T<:AbstractFloat}(start::T, step::T, stop::T) end end end - len = max(0, floor(r) + 1) - stop′ = start + len*step - len += (start < stop′ <= stop) + (start > stop′ >= stop) + if r < 0 + len = 0 + elseif r == 0 + len = 1 + else + len = round(Int, r) + 1 + stop′ = start + (len-1)*step + # if we've overshot the end, subtract one: + len -= (start < stop < stop′) + (start > stop > stop′) + end FloatRange{T}(start, step, len, one(step)) end diff --git a/test/ranges.jl b/test/ranges.jl index d5ffcbafd3b5b..c9aa06ba35372 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -336,11 +336,15 @@ for T = (Float32, Float64,), i = 1:2^15, n = 1:5 start, step = randn(T), randn(T) step == 0 && continue stop = start + (n-1)*step + # `n` is not necessarily unique s.t. `start + (n-1)*step == stop` + # so test that `length(start:step:stop)` satisfies this identity + # and is the closest value to `(stop-start)/step` to do so lo = hi = n while start + (lo-1)*step == stop; lo -= 1; end while start + (hi-1)*step == stop; hi += 1; end + m = clamp(round(Int, (stop-start)/step) + 1, lo+1, hi-1) r = start:step:stop - @test lo < length(r) < hi + @test m == length(r) @test start == first(r) @test stop == last(r) l = linspace(start,stop,n)