From 72c1e7fe0b80799e319e4d3dc9e3dfc73a39dd47 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 15 Jul 2021 16:03:50 -0400 Subject: [PATCH] add length type parameter to StepRangeLen Also be more careful about using additive identity instead of multiplicative, and be more consistent about types in a few places. Fixes #41517 --- base/broadcast.jl | 21 ++++--- base/range.jl | 111 ++++++++++++++++++++--------------- base/twiceprecision.jl | 129 +++++++++++++++++++++++------------------ test/ranges.jl | 4 +- 4 files changed, 149 insertions(+), 116 deletions(-) diff --git a/base/broadcast.jl b/base/broadcast.jl index c6651e28489a3..b34a73041708b 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -1121,19 +1121,20 @@ end ## scalar-range broadcast operations ## # DefaultArrayStyle and \ are not available at the time of range.jl -broadcasted(::DefaultArrayStyle{1}, ::typeof(+), r::OrdinalRange) = r -broadcasted(::DefaultArrayStyle{1}, ::typeof(+), r::StepRangeLen) = r -broadcasted(::DefaultArrayStyle{1}, ::typeof(+), r::LinRange) = r +broadcasted(::DefaultArrayStyle{1}, ::typeof(+), r::AbstractRange) = r -broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r::OrdinalRange) = range(-first(r), step=-step(r), length=length(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r::AbstractRange) = range(-first(r), step=-step(r), length=length(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r::OrdinalRange) = range(-first(r), -last(r), step=-step(r)) broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r::StepRangeLen) = StepRangeLen(-r.ref, -r.step, length(r), r.offset) broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r::LinRange) = LinRange(-r.start, -r.stop, length(r)) -broadcasted(::DefaultArrayStyle{1}, ::typeof(+), x::Real, r::AbstractUnitRange) = range(x + first(r), length=length(r)) -broadcasted(::DefaultArrayStyle{1}, ::typeof(+), r::AbstractUnitRange, x::Real) = range(first(r) + x, length=length(r)) # For #18336 we need to prevent promotion of the step type: broadcasted(::DefaultArrayStyle{1}, ::typeof(+), r::AbstractRange, x::Number) = range(first(r) + x, step=step(r), length=length(r)) broadcasted(::DefaultArrayStyle{1}, ::typeof(+), x::Number, r::AbstractRange) = range(x + first(r), step=step(r), length=length(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(+), r::OrdinalRange, x::Real) = range(first(r) + x, last(r) + x, step=step(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(+), x::Real, r::Real) = range(x + first(r), x + last(r), step=step(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(+), r::AbstractUnitRange, x::Real) = range(first(r) + x, last(r) + x) +broadcasted(::DefaultArrayStyle{1}, ::typeof(+), x::Real, r::AbstractUnitRange) = range(x + first(r), x + last(r)) broadcasted(::DefaultArrayStyle{1}, ::typeof(+), r::StepRangeLen{T}, x::Number) where T = StepRangeLen{typeof(T(r.ref)+x)}(r.ref + x, r.step, length(r), r.offset) broadcasted(::DefaultArrayStyle{1}, ::typeof(+), x::Number, r::StepRangeLen{T}) where T = @@ -1142,9 +1143,11 @@ broadcasted(::DefaultArrayStyle{1}, ::typeof(+), r::LinRange, x::Number) = LinRa broadcasted(::DefaultArrayStyle{1}, ::typeof(+), x::Number, r::LinRange) = LinRange(x + r.start, x + r.stop, length(r)) broadcasted(::DefaultArrayStyle{1}, ::typeof(+), r1::AbstractRange, r2::AbstractRange) = r1 + r2 -broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r::AbstractUnitRange, x::Number) = range(first(r)-x, length=length(r)) -broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r::AbstractRange, x::Number) = range(first(r)-x, step=step(r), length=length(r)) -broadcasted(::DefaultArrayStyle{1}, ::typeof(-), x::Number, r::AbstractRange) = range(x-first(r), step=-step(r), length=length(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r::AbstractRange, x::Number) = range(first(r) - x, step=step(r), length=length(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(-), x::Number, r::AbstractRange) = range(x - first(r), step=-step(r), length=length(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r::OrdinalRange, x::Real) = range(first(r) - x, last(r) - x, step=step(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(-), x::Real, r::OrdinalRange) = range(x - first(r), x - last(r), step=-step(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r::AbstractUnitRange, x::Real) = range(first(r) - x, last(r) - x) broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r::StepRangeLen{T}, x::Number) where T = StepRangeLen{typeof(T(r.ref)-x)}(r.ref - x, r.step, length(r), r.offset) broadcasted(::DefaultArrayStyle{1}, ::typeof(-), x::Number, r::StepRangeLen{T}) where T = diff --git a/base/range.jl b/base/range.jl index 355ee2d80bce2..640afbfafd5fb 100644 --- a/base/range.jl +++ b/base/range.jl @@ -24,9 +24,9 @@ _colon(::Ordered, ::Any, start::T, step, stop::T) where {T} = StepRange(start, step, stop) # for T<:Union{Float16,Float32,Float64} see twiceprecision.jl _colon(::Ordered, ::ArithmeticRounds, start::T, step, stop::T) where {T} = - StepRangeLen(start, step, floor(Int, (stop-start)/step)+1) + StepRangeLen(start, step, floor(Integer, (stop-start)/step)+1) _colon(::Any, ::Any, start::T, step, stop::T) where {T} = - StepRangeLen(start, step, floor(Int, (stop-start)/step)+1) + StepRangeLen(start, step, floor(Integer, (stop-start)/step)+1) """ (:)(start, [step], stop) @@ -415,8 +415,9 @@ oneto(r) = OneTo(r) ## Step ranges parameterized by length """ - StepRangeLen{T,R,S}(ref::R, step::S, len, [offset=1]) where {T,R,S} - StepRangeLen( ref::R, step::S, len, [offset=1]) where { R,S} + StepRangeLen( ref::R, step::S, len, [offset=1]) where { R,S} + StepRangeLen{T,R,S}( ref::R, step::S, len, [offset=1]) where {T,R,S} + StepRangeLen{T,R,S,L}(ref::R, step::S, len, [offset=1]) where {T,R,S,L} A range `r` where `r[i]` produces values of type `T` (in the second form, `T` is deduced automatically), parameterized by a `ref`erence @@ -426,26 +427,30 @@ value `r[1]`, but alternatively you can supply it as the value of with `TwicePrecision` this can be used to implement ranges that are free of roundoff error. """ -struct StepRangeLen{T,R,S} <: AbstractRange{T} +struct StepRangeLen{T,R,S,L} <: AbstractRange{T} ref::R # reference value (might be smallest-magnitude value in the range) step::S # step value - len::Int # length of the range - offset::Int # the index of ref + len::L # length of the range + offset::L # the index of ref - function StepRangeLen{T,R,S}(ref::R, step::S, len::Integer, offset::Integer = 1) where {T,R,S} + function StepRangeLen{T,R,S,L}(ref::R, step::S, len::Integer, offset::Integer = 1) where {T,R,S,L} if T <: Integer && !isinteger(ref + step) throw(ArgumentError("StepRangeLen{<:Integer} cannot have non-integer step")) end + len = convert(L, len) len >= 0 || throw(ArgumentError("length cannot be negative, got $len")) - 1 <= offset <= max(1,len) || throw(ArgumentError("StepRangeLen: offset must be in [1,$len], got $offset")) - new(ref, step, len, offset) + offset = convert(L, offset) + 1 <= offset <= max(1, len) || throw(ArgumentError("StepRangeLen: offset must be in [1,$len], got $offset")) + return new(ref, step, len, offset) end end +StepRangeLen{T,R,S}(ref::R, step::S, len::Integer, offset::Integer = 1) where {T,R,S} = + StepRangeLen{T,R,S,promote_type(Int,typeof(len))}(ref, step, len, offset) StepRangeLen(ref::R, step::S, len::Integer, offset::Integer = 1) where {R,S} = - StepRangeLen{typeof(ref+zero(step)),R,S}(ref, step, len, offset) + StepRangeLen{typeof(ref+zero(step)),R,S,promote_type(Int,typeof(len))}(ref, step, len, offset) StepRangeLen{T}(ref::R, step::S, len::Integer, offset::Integer = 1) where {T,R,S} = - StepRangeLen{T,R,S}(ref, step, len, offset) + StepRangeLen{T,R,S,promote_type(Int,typeof(len))}(ref, step, len, offset) ## range with computed step @@ -621,6 +626,7 @@ step(r::StepRangeLen) = r.step step(r::StepRangeLen{T}) where {T<:AbstractFloat} = T(r.step) step(r::LinRange) = (last(r)-first(r))/r.lendiv +# high-precision step step_hp(r::StepRangeLen) = r.step step_hp(r::AbstractRange) = step(r) @@ -648,7 +654,7 @@ function checked_length(r::OrdinalRange{T}) where T diff = checked_sub(stop, start) end a = Integer(div(diff, s)) - return checked_add(a, one(a)) + return checked_add(a, oneunit(a)) end function checked_length(r::AbstractUnitRange{T}) where T @@ -657,7 +663,7 @@ function checked_length(r::AbstractUnitRange{T}) where T return Integer(first(r) - first(r)) end a = Integer(checked_add(checked_sub(last(r), first(r)))) - return checked_add(a, one(a)) + return checked_add(a, oneunit(a)) end function length(r::OrdinalRange{T}) where T @@ -675,14 +681,14 @@ function length(r::OrdinalRange{T}) where T diff = stop - start end a = Integer(div(diff, s)) - return a + one(a) + return a + oneunit(a) end function length(r::AbstractUnitRange{T}) where T @_inline_meta a = Integer(last(r) - first(r)) # even when isempty, by construction (with overflow) - return a + one(a) + return a + oneunit(a) end length(r::OneTo) = Integer(r.stop - zero(r.stop)) @@ -710,7 +716,7 @@ let bigints = Union{Int, UInt, Int64, UInt64, Int128, UInt128} else a = div(unsigned(diff), s) % typeof(diff) end - return Integer(a) + one(a) + return Integer(a) + oneunit(a) end function checked_length(r::OrdinalRange{T}) where T<:bigints s = step(r) @@ -729,7 +735,7 @@ let bigints = Union{Int, UInt, Int64, UInt64, Int128, UInt128} else a = div(checked_sub(start, stop), -s) end - return checked_add(a, one(a)) + return checked_add(a, oneunit(a)) end end @@ -803,7 +809,13 @@ copy(r::AbstractRange) = r ## iteration -function iterate(r::Union{LinRange,StepRangeLen}, i::Int=1) +function iterate(r::StepRangeLen, i::Integer=1) + @_inline_meta + length(r) < i && return nothing + unsafe_getindex(r, i), i + 1 +end + +function iterate(r::LinRange, i::Int=1) @_inline_meta length(r) < i && return nothing unsafe_getindex(r, i), i + 1 @@ -897,7 +909,7 @@ function getindex(r::AbstractUnitRange, s::AbstractUnitRange{T}) where {T<:Integ @boundscheck checkbounds(r, s) if T === Bool - range(first(s) ? first(r) : last(r), length = Int(last(s))) + range(first(s) ? first(r) : last(r), length = Integer(last(s))) else f = first(r) st = oftype(f, f + first(s)-1) @@ -916,7 +928,7 @@ function getindex(r::AbstractUnitRange, s::StepRange{T}) where {T<:Integer} @boundscheck checkbounds(r, s) if T === Bool - range(first(s) ? first(r) : last(r), step=oneunit(eltype(r)), length = Int(last(s))) + range(first(s) ? first(r) : last(r), step=oneunit(eltype(r)), length = Integer(last(s))) else st = oftype(first(r), first(r) + s.start-1) return range(st, step=step(s), length=length(s)) @@ -949,24 +961,29 @@ function getindex(r::StepRangeLen{T}, s::OrdinalRange{S}) where {T, S<:Integer} @_inline_meta @boundscheck checkbounds(r, s) + len = length(s) + sstep = step_hp(s) + rstep = step_hp(r) + L = typeof(len) if S === Bool - if length(s) == 0 - return StepRangeLen{T}(first(r), step(r), 0, 1) - elseif length(s) == 1 + rstep *= one(sstep) + if len == 0 + return StepRangeLen{T}(first(r), rstep, zero(L), oneunit(L)) + elseif len == 1 if first(s) - return StepRangeLen{T}(first(r), step(r), 1, 1) + return StepRangeLen{T}(first(r), rstep, oneunit(L), oneunit(L)) else - return StepRangeLen{T}(first(r), step(r), 0, 1) + return StepRangeLen{T}(first(r), rstep, zero(L), oneunit(L)) end - else # length(s) == 2 - return StepRangeLen{T}(last(r), step(r), 1, 1) + else # len == 2 + return StepRangeLen{T}(last(r), rstep, oneunit(L), oneunit(L)) end else # Find closest approach to offset by s ind = LinearIndices(s) - offset = max(min(1 + round(Int, (r.offset - first(s))/step(s)), last(ind)), first(ind)) - ref = _getindex_hiprec(r, first(s) + (offset-1)*step(s)) - return StepRangeLen{T}(ref, r.step*step(s), length(s), offset) + offset = L(max(min(1 + round(L, (r.offset - first(s))/sstep), last(ind)), first(ind))) + ref = _getindex_hiprec(r, first(s) + (offset-1)*sstep) + return StepRangeLen{T}(ref, rstep*sstep, len, offset) end end @@ -1153,8 +1170,8 @@ issubset(r::AbstractUnitRange{<:Integer}, s::AbstractUnitRange{<:Integer}) = ## linear operations on ranges ## -(r::OrdinalRange) = range(-first(r), step=-step(r), length=length(r)) --(r::StepRangeLen{T,R,S}) where {T,R,S} = - StepRangeLen{T,R,S}(-r.ref, -r.step, length(r), r.offset) +-(r::StepRangeLen{T,R,S,L}) where {T,R,S,L} = + StepRangeLen{T,R,S,L}(-r.ref, -r.step, r.len, r.offset) function -(r::LinRange) start = -r.start LinRange{typeof(start)}(start, -r.stop, length(r)) @@ -1206,20 +1223,20 @@ StepRange(r::AbstractUnitRange{T}) where {T} = StepRange{T,T}(first(r), step(r), last(r)) (StepRange{T1,T2} where T1)(r::AbstractRange) where {T2} = StepRange{eltype(r),T2}(r) -promote_rule(::Type{StepRangeLen{T1,R1,S1}},::Type{StepRangeLen{T2,R2,S2}}) where {T1,T2,R1,R2,S1,S2} = +promote_rule(::Type{StepRangeLen{T1,R1,S1,L1}},::Type{StepRangeLen{T2,R2,S2,L2}}) where {T1,T2,R1,R2,S1,S2,L1,L2} = el_same(promote_type(T1,T2), - StepRangeLen{T1,promote_type(R1,R2),promote_type(S1,S2)}, - StepRangeLen{T2,promote_type(R1,R2),promote_type(S1,S2)}) -StepRangeLen{T,R,S}(r::StepRangeLen{T,R,S}) where {T,R,S} = r -StepRangeLen{T,R,S}(r::StepRangeLen) where {T,R,S} = - StepRangeLen{T,R,S}(convert(R, r.ref), convert(S, r.step), length(r), r.offset) + StepRangeLen{T1,promote_type(R1,R2),promote_type(S1,S2),promote_type(L1,L2)}, + StepRangeLen{T2,promote_type(R1,R2),promote_type(S1,S2),promote_type(L1,L2)}) +StepRangeLen{T,R,S,L}(r::StepRangeLen{T,R,S,L}) where {T,R,S,L} = r +StepRangeLen{T,R,S,L}(r::StepRangeLen) where {T,R,S,L} = + StepRangeLen{T,R,S,L}(convert(R, r.ref), convert(S, r.step), convert(L, r.len), convert(L, r.offset)) StepRangeLen{T}(r::StepRangeLen) where {T} = - StepRangeLen(convert(T, r.ref), convert(T, r.step), length(r), r.offset) + StepRangeLen(convert(T, r.ref), convert(T, r.step), r.len, r.offset) -promote_rule(a::Type{StepRangeLen{T,R,S}}, ::Type{OR}) where {T,R,S,OR<:AbstractRange} = - promote_rule(a, StepRangeLen{eltype(OR), eltype(OR), eltype(OR)}) -StepRangeLen{T,R,S}(r::AbstractRange) where {T,R,S} = - StepRangeLen{T,R,S}(R(first(r)), S(step(r)), length(r)) +promote_rule(a::Type{StepRangeLen{T,R,S,L}}, ::Type{OR}) where {T,R,S,L,OR<:AbstractRange} = + promote_rule(a, StepRangeLen{eltype(OR), eltype(OR), eltype(OR), Int}) +StepRangeLen{T,R,S,L}(r::AbstractRange) where {T,R,S,L} = + StepRangeLen{T,R,S,L}(R(first(r)), S(step(r)), length(r)) StepRangeLen{T}(r::AbstractRange) where {T} = StepRangeLen(T(first(r)), T(step(r)), length(r)) StepRangeLen(r::AbstractRange) = StepRangeLen{eltype(r)}(r) @@ -1233,8 +1250,8 @@ LinRange(r::AbstractRange{T}) where {T} = LinRange{T}(r) promote_rule(a::Type{LinRange{T}}, ::Type{OR}) where {T,OR<:OrdinalRange} = promote_rule(a, LinRange{eltype(OR)}) -promote_rule(::Type{LinRange{L}}, b::Type{StepRangeLen{T,R,S}}) where {L,T,R,S} = - promote_rule(StepRangeLen{L,L,L}, b) +promote_rule(::Type{LinRange{A}}, b::Type{StepRangeLen{T,R,S,L}}) where {A,T,R,S,L} = + promote_rule(StepRangeLen{A,A,A,Int}, b) ## concatenation ## @@ -1261,7 +1278,7 @@ function _reverse(r::StepRangeLen, ::Colon) # invalid. As `reverse(r)` is also empty, any offset would work so we keep # `r.offset` offset = isempty(r) ? r.offset : length(r)-r.offset+1 - StepRangeLen(r.ref, -r.step, length(r), offset) + return typeof(r)(r.ref, -r.step, length(r), offset) end _reverse(r::LinRange{T}, ::Colon) where {T} = LinRange{T}(r.stop, r.start, length(r)) diff --git a/base/twiceprecision.jl b/base/twiceprecision.jl index 55cdc59371674..ea4bea7537015 100644 --- a/base/twiceprecision.jl +++ b/base/twiceprecision.jl @@ -329,13 +329,13 @@ function steprangelen_hp(::Type{Float64}, ref::Tuple{Integer,Integer}, step::Tuple{Integer,Integer}, nb::Integer, len::Integer, offset::Integer) StepRangeLen(TwicePrecision{Float64}(ref), - TwicePrecision{Float64}(step, nb), Int(len), offset) + TwicePrecision{Float64}(step, nb), len, offset) end function steprangelen_hp(::Type{T}, ref::Tuple{Integer,Integer}, step::Tuple{Integer,Integer}, nb::Integer, len::Integer, offset::Integer) where {T<:IEEEFloat} - StepRangeLen{T}(ref[1]/ref[2], step[1]/step[2], Int(len), offset) + StepRangeLen{T}(ref[1]/ref[2], step[1]/step[2], len, offset) end # AbstractFloat constructors (can supply a single number or a 2-tuple @@ -347,14 +347,13 @@ function steprangelen_hp(::Type{Float64}, ref::F_or_FF, step::F_or_FF, nb::Integer, len::Integer, offset::Integer) StepRangeLen(TwicePrecision{Float64}(ref...), - twiceprecision(TwicePrecision{Float64}(step...), nb), Int(len), offset) + twiceprecision(TwicePrecision{Float64}(step...), nb), len, offset) end function steprangelen_hp(::Type{T}, ref::F_or_FF, step::F_or_FF, nb::Integer, len::Integer, offset::Integer) where {T<:IEEEFloat} - StepRangeLen{T}(asF64(ref), - asF64(step), Int(len), offset) + StepRangeLen{T}(asF64(ref), asF64(step), len, offset) end @@ -365,30 +364,32 @@ StepRangeLen(ref::TwicePrecision{T}, step::TwicePrecision{T}, # Construct range for rational start=start_n/den, step=step_n/den function floatrange(::Type{T}, start_n::Integer, step_n::Integer, len::Integer, den::Integer) where T + len = len + 0 # promote with Int if len < 2 || step_n == 0 - return steprangelen_hp(T, (start_n, den), (step_n, den), 0, Int(len), 1) + return steprangelen_hp(T, (start_n, den), (step_n, den), 0, len, 1) end # index of smallest-magnitude value - imin = clamp(round(Int, -start_n/step_n+1), 1, Int(len)) + imin = clamp(round(typeof(len), -start_n/step_n+1), 1, len) # Compute smallest-magnitude element to 2x precision ref_n = start_n+(imin-1)*step_n # this shouldn't overflow, so don't check nb = nbitslen(T, len, imin) - steprangelen_hp(T, (ref_n, den), (step_n, den), nb, Int(len), imin) + steprangelen_hp(T, (ref_n, den), (step_n, den), nb, len, imin) end function floatrange(a::AbstractFloat, st::AbstractFloat, len::Real, divisor::AbstractFloat) + len = len + 0 # promote with Int T = promote_type(typeof(a), typeof(st), typeof(divisor)) m = maxintfloat(T, Int) if abs(a) <= m && abs(st) <= m && abs(divisor) <= m ia, ist, idivisor = round(Int, a), round(Int, st), round(Int, divisor) if ia == a && ist == st && idivisor == divisor # We can return the high-precision range - return floatrange(T, ia, ist, Int(len), idivisor) + return floatrange(T, ia, ist, len, idivisor) end end # Fallback (misses the opportunity to set offset different from 1, # but otherwise this is still high-precision) - steprangelen_hp(T, (a,divisor), (st,divisor), nbitslen(T, len, 1), Int(len), 1) + steprangelen_hp(T, (a,divisor), (st,divisor), nbitslen(T, len, 1), len, 1) end function (:)(start::T, step::T, stop::T) where T<:Union{Float16,Float32,Float64} @@ -407,7 +408,7 @@ function (:)(start::T, step::T, stop::T) where T<:Union{Float16,Float32,Float64} rem(den, start_d) == 0 && rem(den, step_d) == 0 # check lcm overflow start_n = round(Int, start*den) step_n = round(Int, step*den) - len = max(0, div(den*stop_n - stop_d*start_n + step_n*stop_d, step_n*stop_d)) + len = max(0, Int(div(den*stop_n - stop_d*start_n + step_n*stop_d, step_n*stop_d))) # Integer ops could overflow, so check that this makes sense if isbetween(start, start + (len-1)*step, stop + step/2) && !isbetween(start, start + len*step, stop) @@ -418,6 +419,7 @@ function (:)(start::T, step::T, stop::T) where T<:Union{Float16,Float32,Float64} end end # Fallback, taking start and step literally + # n.b. we use Int as the default length type for IEEEFloats lf = (stop-start)/step if lf < 0 len = 0 @@ -436,6 +438,7 @@ step(r::StepRangeLen{T,TwicePrecision{T},TwicePrecision{T}}) where {T<:AbstractF step(r::StepRangeLen{T,TwicePrecision{T},TwicePrecision{T}}) where {T} = T(r.step) function range_start_step_length(a::T, st::T, len::Integer) where T<:Union{Float16,Float32,Float64} + len = len + 0 # promote with Int start_n, start_d = rat(a) step_n, step_d = rat(st) if start_d != 0 && step_d != 0 && @@ -474,31 +477,37 @@ end function getindex(r::StepRangeLen{T,<:TwicePrecision,<:TwicePrecision}, s::OrdinalRange{S}) where {T, S<:Integer} @boundscheck checkbounds(r, s) + len = length(s) + L = typeof(len) + sstep = step_hp(s) + rstep = step_hp(r) if S === Bool - if length(s) == 0 - return StepRangeLen(r.ref, r.step, 0, 1) - elseif length(s) == 1 + #rstep *= one(sstep) + if len == 0 + return StepRangeLen{T}(first(r), rstep, zero(L), oneunit(L)) + elseif len == 1 if first(s) - return StepRangeLen(r.ref, r.step, 1, 1) + return StepRangeLen{T}(first(r), rstep, oneunit(L), oneunit(L)) else - return StepRangeLen(r.ref, r.step, 0, 1) + return StepRangeLen{T}(first(r), rstep, zero(L), oneunit(L)) end - else # length(s) == 2 - return StepRangeLen(r[2], step(r), 1, 1) + else # len == 2 + return StepRangeLen{T}(last(r), step(r), oneunit(L), oneunit(L)) end else - soffset = 1 + round(Int, (r.offset - first(s))/step(s)) - soffset = clamp(soffset, 1, length(s)) - ioffset = first(s) + (soffset-1)*step(s) - if step(s) == 1 || length(s) < 2 - newstep = r.step + soffset = 1 + round(L, (r.offset - first(s))/sstep) + soffset = L(clamp(soffset, 1, len)) + ioffset = L(first(s) + (soffset-1)*sstep) + if sstep == 1 || len < 2 + newstep = rstep #* one(sstep) else - newstep = twiceprecision(r.step*step(s), nbitslen(T, length(s), soffset)) + newstep = rstep * sstep + newstep = twiceprecision(newstep, nbitslen(T, len, soffset)) end if ioffset == r.offset - return StepRangeLen(r.ref, newstep, length(s), max(1,soffset)) + return StepRangeLen{T}(r.ref, newstep, len, max(1, soffset)) else - return StepRangeLen(r.ref + (ioffset-r.offset)*r.step, newstep, length(s), max(1,soffset)) + return StepRangeLen{T}(r.ref + (ioffset-r.offset)*rstep, newstep, len, max(1, soffset)) end end end @@ -509,30 +518,30 @@ end /(r::StepRangeLen{<:Real,<:TwicePrecision}, x::Real) = StepRangeLen(r.ref/x, twiceprecision(r.step/x, nbitslen(r)), length(r), r.offset) -StepRangeLen{T,R,S}(r::StepRangeLen{T,R,S}) where {T<:AbstractFloat,R<:TwicePrecision,S<:TwicePrecision} = r +StepRangeLen{T,R,S,L}(r::StepRangeLen{T,R,S,L}) where {T<:AbstractFloat,R<:TwicePrecision,S<:TwicePrecision,L} = r -StepRangeLen{T,R,S}(r::StepRangeLen) where {T<:AbstractFloat,R<:TwicePrecision,S<:TwicePrecision} = - _convertSRL(StepRangeLen{T,R,S}, r) +StepRangeLen{T,R,S,L}(r::StepRangeLen) where {T<:AbstractFloat,R<:TwicePrecision,S<:TwicePrecision,L} = + _convertSRL(StepRangeLen{T,R,S,L}, r) StepRangeLen{Float64}(r::StepRangeLen) = - _convertSRL(StepRangeLen{Float64,TwicePrecision{Float64},TwicePrecision{Float64}}, r) + _convertSRL(StepRangeLen{Float64,TwicePrecision{Float64},TwicePrecision{Float64},Int}, r) StepRangeLen{T}(r::StepRangeLen) where {T<:IEEEFloat} = - _convertSRL(StepRangeLen{T,Float64,Float64}, r) + _convertSRL(StepRangeLen{T,Float64,Float64,Int}, r) StepRangeLen{Float64}(r::AbstractRange) = - _convertSRL(StepRangeLen{Float64,TwicePrecision{Float64},TwicePrecision{Float64}}, r) + _convertSRL(StepRangeLen{Float64,TwicePrecision{Float64},TwicePrecision{Float64},Int}, r) StepRangeLen{T}(r::AbstractRange) where {T<:IEEEFloat} = - _convertSRL(StepRangeLen{T,Float64,Float64}, r) + _convertSRL(StepRangeLen{T,Float64,Float64,Int}, r) -function _convertSRL(::Type{StepRangeLen{T,R,S}}, r::StepRangeLen{<:Integer}) where {T,R,S} - StepRangeLen{T,R,S}(R(r.ref), S(r.step), length(r), r.offset) +function _convertSRL(::Type{StepRangeLen{T,R,S,L}}, r::StepRangeLen{<:Integer}) where {T,R,S,L} + StepRangeLen{T,R,S,L}(R(r.ref), S(r.step), L(length(r)), L(r.offset)) end -function _convertSRL(::Type{StepRangeLen{T,R,S}}, r::AbstractRange{<:Integer}) where {T,R,S} - StepRangeLen{T,R,S}(R(first(r)), S(step(r)), length(r)) +function _convertSRL(::Type{StepRangeLen{T,R,S,L}}, r::AbstractRange{<:Integer}) where {T,R,S,L} + StepRangeLen{T,R,S,L}(R(first(r)), S(step(r)), L(length(r))) end -function _convertSRL(::Type{StepRangeLen{T,R,S}}, r::AbstractRange{U}) where {T,R,S,U} +function _convertSRL(::Type{StepRangeLen{T,R,S,L}}, r::AbstractRange{U}) where {T,R,S,L,U} # if start and step have a rational approximation in the old type, # then we transfer that rational approximation to the new type f, s = first(r), step(r) @@ -546,17 +555,17 @@ function _convertSRL(::Type{StepRangeLen{T,R,S}}, r::AbstractRange{U}) where {T, rem(den, start_d) == 0 && rem(den, step_d) == 0 start_n = round(Int, f*den) step_n = round(Int, s*den) - return floatrange(T, start_n, step_n, length(r), den) + return floatrange(T, start_n, step_n, L(length(r)), den) end end - __convertSRL(StepRangeLen{T,R,S}, r) + return __convertSRL(StepRangeLen{T,R,S,L}, r) end -function __convertSRL(::Type{StepRangeLen{T,R,S}}, r::StepRangeLen{U}) where {T,R,S,U} - StepRangeLen{T,R,S}(R(r.ref), S(r.step), length(r), r.offset) +function __convertSRL(::Type{StepRangeLen{T,R,S,L}}, r::StepRangeLen{U}) where {T,R,S,L,U} + StepRangeLen{T,R,S,L}(R(r.ref), S(r.step), L(length(r)), L(r.offset)) end -function __convertSRL(::Type{StepRangeLen{T,R,S}}, r::AbstractRange{U}) where {T,R,S,U} - StepRangeLen{T,R,S}(R(first(r)), S(step(r)), length(r)) +function __convertSRL(::Type{StepRangeLen{T,R,S,L}}, r::AbstractRange{U}) where {T,R,S,L,U} + StepRangeLen{T,R,S,L}(R(first(r)), S(step(r)), L(length(r))) end function sum(r::StepRangeLen) @@ -603,19 +612,20 @@ function +(r1::StepRangeLen{T,R}, r2::StepRangeLen{T,R}) where T where R<:TwiceP imid = r1.offset ref = r1.ref + r2.ref else - imid = round(Int, (r1.offset+r2.offset)/2) + imid = round(typeof(len), (r1.offset+r2.offset)/2) ref1mid = _getindex_hiprec(r1, imid) ref2mid = _getindex_hiprec(r2, imid) ref = ref1mid + ref2mid end step = twiceprecision(r1.step + r2.step, nbitslen(T, len, imid)) - StepRangeLen{T,typeof(ref),typeof(step)}(ref, step, len, imid) + StepRangeLen{T,typeof(ref),typeof(step),typeof(len)}(ref, step, len, imid) end ## LinRange # For Float16, Float32, and Float64, this returns a StepRangeLen function range_start_stop_length(start::T, stop::T, len::Integer) where {T<:IEEEFloat} + len = len + 0 # promote with Int len < 2 && return _linspace1(T, start, stop, len) if start == stop return steprangelen_hp(T, start, zero(T), 0, len, 1) @@ -638,25 +648,26 @@ function range_start_stop_length(start::T, stop::T, len::Integer) where {T<:IEEE end function _linspace(start::T, stop::T, len::Integer) where {T<:IEEEFloat} + len = len + 0 # promote with Int (isfinite(start) && isfinite(stop)) || throw(ArgumentError("start and stop must be finite, got $start and $stop")) # Find the index that returns the smallest-magnitude element Δ, Δfac = stop-start, 1 if !isfinite(Δ) # handle overflow for large endpoints - Δ, Δfac = stop/len - start/len, Int(len) + Δ, Δfac = stop/len - start/len, len end tmin = -(start/Δ)/Δfac # t such that (1-t)*start + t*stop == 0 - imin = round(Int, tmin*(len-1)+1) # index approximately corresponding to t + imin = round(typeof(len), tmin*(len-1)+1) # index approximately corresponding to t if 1 < imin < len # The smallest-magnitude element is in the interior t = (imin-1)/(len-1) ref = T((1-t)*start + t*stop) step = imin-1 < len-imin ? (ref-start)/(imin-1) : (stop-ref)/(len-imin) elseif imin <= 1 - imin = 1 + imin = oneunit(typeof(len)) ref = start step = (Δ/(len-1))*Δfac else - imin = Int(len) + imin = len ref = stop step = (Δ/(len-1))*Δfac end @@ -676,23 +687,25 @@ function _linspace(start::T, stop::T, len::Integer) where {T<:IEEEFloat} a, b = (start - x1_hi) - x1_lo, (stop - x2_hi) - x2_lo step_lo = (b - a)/(len - 1) ref_lo = a - (1 - imin)*step_lo - steprangelen_hp(T, (ref, ref_lo), (step_hi, step_lo), 0, Int(len), imin) + steprangelen_hp(T, (ref, ref_lo), (step_hi, step_lo), 0, len, imin) end # range for rational numbers, start = start_n/den, stop = stop_n/den # Note this returns a StepRangeLen _linspace(::Type{T}, start::Integer, stop::Integer, len::Integer) where {T<:IEEEFloat} = _linspace(T, start, stop, len, one(start)) function _linspace(::Type{T}, start_n::Integer, stop_n::Integer, len::Integer, den::Integer) where T<:IEEEFloat + len = len + 0 # promote with Int len < 2 && return _linspace1(T, start_n/den, stop_n/den, len) start_n == stop_n && return steprangelen_hp(T, (start_n, den), (zero(start_n), den), 0, len, 1) tmin = -start_n/(Float64(stop_n) - Float64(start_n)) - imin = round(Int, tmin*(len-1)+1) - imin = clamp(imin, 1, Int(len)) + imin = round(typeof(len), tmin*(len-1)+1) + imin = clamp(imin, 1, len) + # TODO: why Int128 (instead of `widen(len)`)? ref_num = Int128(len-imin) * start_n + Int128(imin-1) * stop_n ref_denom = Int128(len-1) * den ref = (ref_num, ref_denom) step_full = (Int128(stop_n) - Int128(start_n), ref_denom) - steprangelen_hp(T, ref, step_full, nbitslen(T, len, imin), Int(len), imin) + steprangelen_hp(T, ref, step_full, nbitslen(T, len, imin), len, imin) end # For len < 2 @@ -704,7 +717,7 @@ function _linspace1(::Type{T}, start, stop, len::Integer) where T<:IEEEFloat # The output type must be consistent with steprangelen_hp if T<:Union{Float32,Float16} return StepRangeLen{T}(Float64(start), Float64(start) - Float64(stop), len, 1) - else + else # T == Float64 return StepRangeLen(TwicePrecision(start, zero(T)), TwicePrecision(start, -stop), len, 1) end end @@ -713,8 +726,8 @@ end ### Numeric utilities -# Approximate x with a rational representation. Guaranteed to return, -# but not guaranteed to return a precise answer. +# Approximate x with a rational representation as a pair of Int values. +# Guaranteed to return, but not guaranteed to return a precise answer. # https://en.wikipedia.org/wiki/Continued_fraction#Best_rational_approximations function rat(x) y = x @@ -722,7 +735,7 @@ function rat(x) b = c = 0 m = maxintfloat(narrow(typeof(x)), Int) while abs(y) <= m - f = trunc(Int,y) + f = trunc(Int, y) y -= f a, c = f*a + c, a b, d = f*b + d, b diff --git a/test/ranges.jl b/test/ranges.jl index b622fc05b3ca6..ee79bd160765f 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -652,8 +652,8 @@ end @test broadcast(-, T(1):2:6, 0.3) === T(1)-0.3:2:5-0.3 is_unsigned = T <: Unsigned is_unsigned && @test length(broadcast(-, T(1):3, 2)) === length(T(1)-2:T(3)-2) - @test broadcast(-, T(1):3) == -T(1):-1:-T(3) broken=is_unsigned - @test broadcast(-, 2, T(1):3) == T(1):-1:-T(1) broken=is_unsigned + @test broadcast(-, T(1):3) == -T(1):-T(1):-T(3) + @test broadcast(-, 2, T(1):3) == T(1):-T(1):-T(1) end @testset "operations between ranges and arrays" for T in (Int, UInt, Int128) @test all(([T(1):5;] + (T(5):-1:1)) .=== T(6))