Skip to content

Commit d41c800

Browse files
committed
Make LinSpace generic and endpoint-preserving. Fixes #14420.
1 parent 0a0c41c commit d41c800

8 files changed

+182
-128
lines changed

base/abstractarray.jl

+1-3
Original file line numberDiff line numberDiff line change
@@ -756,9 +756,7 @@ map{T<:Real}(::Type{T}, r::StepRange) = T(r.start):T(r.step):T(last(r))
756756
map{T<:Real}(::Type{T}, r::UnitRange) = T(r.start):T(last(r))
757757
map{T<:AbstractFloat}(::Type{T}, r::FloatRange) = FloatRange(T(r.start), T(r.step), r.len, T(r.divisor))
758758
function map{T<:AbstractFloat}(::Type{T}, r::LinSpace)
759-
new_len = T(r.len)
760-
new_len == r.len || error("$r: too long for $T")
761-
LinSpace(T(r.start), T(r.stop), new_len, T(r.divisor))
759+
LinSpace(T(r.start), T(r.stop), length(r))
762760
end
763761

764762
## unsafe/pointer conversions ##

base/abstractarraymath.jl

+4
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,10 @@ imag{T<:Real}(x::AbstractArray{T}) = zero(x)
9292
+{T<:Number}(x::AbstractArray{T}) = x
9393
*{T<:Number}(x::AbstractArray{T,2}) = x
9494

95+
function lerpi(j::Integer, d::Integer, A::AbstractArray, B::AbstractArray)
96+
broadcast((a,b) -> lerpi(j, d, a, b), A, B)
97+
end
98+
9599
# index A[:,:,...,i,:,:,...] where "i" is in dimension "d"
96100

97101
"""

base/float.jl

+1-3
Original file line numberDiff line numberDiff line change
@@ -741,9 +741,7 @@ for fn in (:float,:big)
741741
$fn(r::UnitRange) = $fn(r.start):$fn(last(r))
742742
$fn(r::FloatRange) = FloatRange($fn(r.start), $fn(r.step), r.len, $fn(r.divisor))
743743
function $fn(r::LinSpace)
744-
new_len = $fn(r.len)
745-
new_len == r.len || error(string(r, ": too long for ", $fn))
746-
LinSpace($fn(r.start), $fn(r.stop), new_len, $fn(r.divisor))
744+
LinSpace($fn(r.start), $fn(r.stop), length(r))
747745
end
748746
end
749747
end

base/mpfr.jl

+5
Original file line numberDiff line numberDiff line change
@@ -924,4 +924,9 @@ function Base.deepcopy_internal(x::BigFloat, stackdict::ObjectIdDict)
924924
return y
925925
end
926926

927+
function lerpi(j::Integer, d::Integer, a::BigFloat, b::BigFloat)
928+
t = BigFloat(j)/d
929+
fma(t, b, fma(-t, a, a))
930+
end
931+
927932
end #module

base/operators.jl

+2-8
Original file line numberDiff line numberDiff line change
@@ -865,14 +865,8 @@ for f in (:+, :-)
865865
len = r1.len
866866
(len == r2.len ||
867867
throw(DimensionMismatch("argument dimensions must match")))
868-
divisor1, divisor2 = r1.divisor, r2.divisor
869-
if divisor1 == divisor2
870-
LinSpace{T}($f(r1.start, r2.start), $f(r1.stop, r2.stop),
871-
len, divisor1)
872-
else
873-
linspace(convert(T, $f(first(r1), first(r2))),
874-
convert(T, $f(last(r1), last(r2))), len)
875-
end
868+
linspace(convert(T, $f(first(r1), first(r2))),
869+
convert(T, $f(last(r1), last(r2))), len)
876870
end
877871

878872
$f(r1::Union{FloatRange, OrdinalRange, LinSpace},

base/range.jl

+90-77
Original file line numberDiff line numberDiff line change
@@ -209,68 +209,29 @@ range(a::AbstractFloat, st::Real, len::Integer) = FloatRange(a, float(st), len,
209209

210210
## linspace and logspace
211211

212-
immutable LinSpace{T<:AbstractFloat} <: Range{T}
212+
immutable LinSpace{T} <: Range{T}
213213
start::T
214214
stop::T
215-
len::T
216-
divisor::T
217-
end
218-
219-
function linspace{T<:AbstractFloat}(start::T, stop::T, len::T)
220-
len == round(len) || throw(InexactError())
221-
0 <= len || error("linspace($start, $stop, $len): negative length")
222-
if len == 0
223-
n = convert(T, 2)
224-
if isinf(n*start) || isinf(n*stop)
225-
start /= n; stop /= n; n = one(T)
215+
len::Int
216+
lendiv::Int
217+
218+
function LinSpace(start,stop,len)
219+
len >= 0 || error("linspace($start, $stop, $len): negative length")
220+
if len == 1
221+
start == stop || error("linspace($start, $stop, $len): endpoints differ")
222+
return new(start, stop, 1, 1)
226223
end
227-
return LinSpace(-start, -stop, -one(T), n)
224+
new(start,stop,len,max(len-1,1))
228225
end
229-
if len == 1
230-
start == stop || error("linspace($start, $stop, $len): endpoints differ")
231-
return LinSpace(-start, -start, zero(T), one(T))
232-
end
233-
n = convert(T, len - 1)
234-
len - n == 1 || error("linspace($start, $stop, $len): too long for $T")
235-
a0, b = rat(start)
236-
a = convert(T,a0)
237-
if a/convert(T,b) == start
238-
c0, d = rat(stop)
239-
c = convert(T,c0)
240-
if c/convert(T,d) == stop
241-
e = lcm(b,d)
242-
a *= div(e,b)
243-
c *= div(e,d)
244-
s = convert(T,n*e)
245-
if isinf(a*n) || isinf(c*n)
246-
s, p = frexp(s)
247-
p2 = oftype(s,2)^p
248-
a /= p2; c /= p2
249-
end
250-
if a*n/s == start && c*n/s == stop
251-
return LinSpace(a, c, len, s)
252-
end
253-
end
254-
end
255-
a, c, s = start, stop, n
256-
if isinf(a*n) || isinf(c*n)
257-
s, p = frexp(s)
258-
p2 = oftype(s,2)^p
259-
a /= p2; c /= p2
260-
end
261-
if a*n/s == start && c*n/s == stop
262-
return LinSpace(a, c, len, s)
263-
end
264-
return LinSpace(start, stop, len, n)
265226
end
266-
function linspace{T<:AbstractFloat}(start::T, stop::T, len::Real)
267-
T_len = convert(T, len)
268-
T_len == len || throw(InexactError())
269-
linspace(start, stop, T_len)
227+
228+
function LinSpace(start, stop, len::Integer)
229+
T = typeof((stop-start)/len)
230+
LinSpace{T}(start, stop, len)
270231
end
271232

272233
"""
273-
linspace(start::Real, stop::Real, n::Real=50)
234+
linspace(start, stop, n=50)
274235
275236
Construct a range of `n` linearly spaced elements from `start` to `stop`.
276237
@@ -280,8 +241,7 @@ julia> linspace(1.3,2.9,9)
280241
1.3,1.5,1.7,1.9,2.1,2.3,2.5,2.7,2.9
281242
```
282243
"""
283-
linspace(start::Real, stop::Real, len::Real=50) =
284-
linspace(promote(AbstractFloat(start), AbstractFloat(stop))..., len)
244+
linspace(start, stop, len::Real=50) = LinSpace(start, stop, Int(len))
285245

286246
function show(io::IO, r::LinSpace)
287247
print(io, "linspace(")
@@ -398,7 +358,7 @@ julia> step(linspace(2.5,10.9,85))
398358
step(r::StepRange) = r.step
399359
step(r::AbstractUnitRange) = 1
400360
step(r::FloatRange) = r.step/r.divisor
401-
step{T}(r::LinSpace{T}) = ifelse(r.len <= 0, convert(T,NaN), (r.stop-r.start)/r.divisor)
361+
step(r::LinSpace) = (last(r)-first(r))/r.lendiv
402362

403363
unsafe_length(r::Range) = length(r) # generic fallback
404364

@@ -412,7 +372,7 @@ unsafe_length(r::OneTo) = r.stop
412372
length(r::AbstractUnitRange) = unsafe_length(r)
413373
length(r::OneTo) = unsafe_length(r)
414374
length(r::FloatRange) = Integer(r.len)
415-
length(r::LinSpace) = Integer(r.len + signbit(r.len - 1))
375+
length(r::LinSpace) = r.len
416376

417377
function length{T<:Union{Int,UInt,Int64,UInt64}}(r::StepRange{T})
418378
isempty(r) && return zero(T)
@@ -452,11 +412,11 @@ end
452412
first{T}(r::OrdinalRange{T}) = convert(T, r.start)
453413
first{T}(r::OneTo{T}) = one(T)
454414
first{T}(r::FloatRange{T}) = convert(T, r.start/r.divisor)
455-
first{T}(r::LinSpace{T}) = convert(T, (r.len-1)*r.start/r.divisor)
415+
first(r::LinSpace) = r.start
456416

457417
last{T}(r::OrdinalRange{T}) = convert(T, r.stop)
458418
last{T}(r::FloatRange{T}) = convert(T, (r.start + (r.len-1)*r.step)/r.divisor)
459-
last{T}(r::LinSpace{T}) = convert(T, (r.len-1)*r.stop/r.divisor)
419+
last(r::LinSpace) = r.stop
460420

461421
minimum(r::AbstractUnitRange) = isempty(r) ? throw(ArgumentError("range must be non-empty")) : first(r)
462422
maximum(r::AbstractUnitRange) = isempty(r) ? throw(ArgumentError("range must be non-empty")) : last(r)
@@ -479,8 +439,10 @@ next{T}(r::FloatRange{T}, i::Int) =
479439

480440
start(r::LinSpace) = 1
481441
done(r::LinSpace, i::Int) = length(r) < i
482-
next{T}(r::LinSpace{T}, i::Int) =
483-
(convert(T, ((r.len-i)*r.start + (i-1)*r.stop)/r.divisor), i+1)
442+
function next(r::LinSpace, i::Int)
443+
@_inline_meta
444+
unsafe_getindex(r, i), i+1
445+
end
484446

485447
start(r::StepRange) = oftype(r.start + r.step, r.start)
486448
next{T}(r::StepRange{T}, i) = (convert(T,i), i+r.step)
@@ -538,10 +500,25 @@ function getindex{T}(r::FloatRange{T}, i::Integer)
538500
convert(T, (r.start + (i-1)*r.step)/r.divisor)
539501
end
540502

541-
function getindex{T}(r::LinSpace{T}, i::Integer)
503+
function getindex(r::LinSpace, i::Integer)
542504
@_inline_meta
543505
@boundscheck checkbounds(r, i)
544-
convert(T, ((r.len-i)*r.start + (i-1)*r.stop)/r.divisor)
506+
unsafe_getindex(r, i)
507+
end
508+
509+
# This is separate to make it useful even when running with --check-bounds=yes
510+
function unsafe_getindex(r::LinSpace, i::Integer)
511+
d = r.lendiv
512+
j, a, b = ifelse(2i >= length(r), (i-1, r.start, r.stop), (length(r)-i, r.stop, r.start))
513+
lerpi(j, d, a, b)
514+
end
515+
516+
# High-precision interpolation. Accurate for t ∈ [0.5,1], so that 1-t is exact.
517+
function lerpi{T}(j::Integer, d::Integer, a::T, b::T)
518+
@_inline_meta
519+
t = j/d
520+
# computes (1-t)*a + t*b
521+
T(fma(t, b, fma(-t, a, a)))
545522
end
546523

547524
getindex(r::Range, ::Colon) = copy(r)
@@ -583,11 +560,11 @@ end
583560
function getindex{T}(r::LinSpace{T}, s::OrdinalRange)
584561
@_inline_meta
585562
@boundscheck checkbounds(r, s)
586-
sl::T = length(s)
563+
sl = length(s)
587564
ifirst = first(s)
588565
ilast = last(s)
589-
vfirst::T = ((r.len - ifirst) * r.start + (ifirst - 1) * r.stop) / r.divisor
590-
vlast::T = ((r.len - ilast) * r.start + (ilast - 1) * r.stop) / r.divisor
566+
vfirst = unsafe_getindex(r, ifirst)
567+
vlast = unsafe_getindex(r, ilast)
591568
return linspace(vfirst, vlast, sl)
592569
end
593570

@@ -741,18 +718,18 @@ end
741718

742719
-(r::OrdinalRange) = range(-first(r), -step(r), length(r))
743720
-(r::FloatRange) = FloatRange(-r.start, -r.step, r.len, r.divisor)
744-
-(r::LinSpace) = LinSpace(-r.start, -r.stop, r.len, r.divisor)
721+
-(r::LinSpace) = LinSpace(-r.start, -r.stop, r.len)
745722

746723
+(x::Real, r::AbstractUnitRange) = range(x + first(r), length(r))
747724
+(x::Real, r::Range) = (x+first(r)):step(r):(x+last(r))
748725
#+(x::Real, r::StepRange) = range(x + r.start, r.step, length(r))
749726
+(x::Real, r::FloatRange) = FloatRange(r.divisor*x + r.start, r.step, r.len, r.divisor)
750-
function +{T}(x::Real, r::LinSpace{T})
751-
x2 = x * r.divisor / (r.len - 1)
752-
LinSpace(x2 + r.start, x2 + r.stop, r.len, r.divisor)
727+
function +(x::Real, r::LinSpace)
728+
LinSpace(x + r.start, x + r.stop, r.len)
729+
end
730+
function +(x::Number, r::LinSpace)
731+
LinSpace(x + r.start, x + r.stop, r.len)
753732
end
754-
+(r::Range, x::Real) = x + r
755-
#+(r::FloatRange, x::Real) = x + r
756733

757734
-(x::Real, r::Range) = (x-first(r)):-step(r):(x-last(r))
758735
-(x::Real, r::FloatRange) = FloatRange(r.divisor*x - r.start, -r.step, r.len, r.divisor)
@@ -778,6 +755,42 @@ end
778755
/(r::OrdinalRange, x::Real) = range(first(r)/x, step(r)/x, length(r))
779756
/(r::FloatRange, x::Real) = FloatRange(r.start/x, r.step/x, r.len, r.divisor)
780757
/(r::LinSpace, x::Real) = LinSpace(r.start / x, r.stop / x, r.len, r.divisor)
758+
=======
759+
.-(x::Real, r::Range) = (x-first(r)):-step(r):(x-last(r))
760+
.-(x::Real, r::FloatRange) = FloatRange(r.divisor*x - r.start, -r.step, r.len, r.divisor)
761+
function .-(x::Real, r::LinSpace)
762+
LinSpace(x - r.start, x - r.stop, r.len)
763+
end
764+
function .-(x::Number, r::LinSpace)
765+
LinSpace(x - r.start, x - r.stop, r.len)
766+
end
767+
function .-{T}(x::Ref{T}, r::LinSpace{T})
768+
LinSpace(x - r.start, x - r.stop, r.len)
769+
end
770+
.-(r::AbstractUnitRange, x::Real) = range(first(r)-x, length(r))
771+
.-(r::StepRange , x::Real) = range(r.start-x, r.step, length(r))
772+
.-(r::FloatRange, x::Real) = FloatRange(r.start - r.divisor*x, r.step, r.len, r.divisor)
773+
function .-(r::LinSpace, x::Real)
774+
LinSpace(r.start - x, r.stop - x, r.len)
775+
end
776+
function .-(r::LinSpace, x::Number)
777+
LinSpace(r.start - x, r.stop - x, r.len)
778+
end
779+
function .-{T}(r::LinSpace{T}, x::Ref{T})
780+
LinSpace(r.start - x, r.stop - x, r.len)
781+
end
782+
783+
.*(x::Real, r::OrdinalRange) = range(x*first(r), x*step(r), length(r))
784+
.*(x::Real, r::FloatRange) = FloatRange(x*r.start, x*r.step, r.len, r.divisor)
785+
.*(x::Real, r::LinSpace) = LinSpace(x * r.start, x * r.stop, r.len)
786+
.*(r::Range, x::Real) = x .* r
787+
.*(r::FloatRange, x::Real) = x .* r
788+
.*(r::LinSpace, x::Real) = x .* r
789+
790+
./(r::OrdinalRange, x::Real) = range(first(r)/x, step(r)/x, length(r))
791+
./(r::FloatRange, x::Real) = FloatRange(r.start/x, r.step/x, r.len, r.divisor)
792+
./(r::LinSpace, x::Real) = LinSpace(r.start / x, r.stop / x, r.len)
793+
>>>>>>> 6f27d2b... Make LinSpace generic and endpoint-preserving. Fixes #14420.
781794

782795
promote_rule{T1,T2}(::Type{UnitRange{T1}},::Type{UnitRange{T2}}) =
783796
UnitRange{promote_type(T1,T2)}
@@ -822,20 +835,20 @@ promote_rule{T1,T2}(::Type{LinSpace{T1}},::Type{LinSpace{T2}}) =
822835
LinSpace{promote_type(T1,T2)}
823836
convert{T<:AbstractFloat}(::Type{LinSpace{T}}, r::LinSpace{T}) = r
824837
convert{T<:AbstractFloat}(::Type{LinSpace{T}}, r::LinSpace) =
825-
LinSpace{T}(r.start, r.stop, r.len, r.divisor)
838+
LinSpace{T}(r.start, r.stop, r.len)
826839

827840
promote_rule{F,OR<:OrdinalRange}(::Type{LinSpace{F}}, ::Type{OR}) =
828841
LinSpace{promote_type(F,eltype(OR))}
829842
convert{T<:AbstractFloat}(::Type{LinSpace{T}}, r::OrdinalRange) =
830-
linspace(convert(T, first(r)), convert(T, last(r)), convert(T, length(r)))
843+
linspace(convert(T, first(r)), convert(T, last(r)), length(r))
831844
convert{T}(::Type{LinSpace}, r::OrdinalRange{T}) =
832845
convert(LinSpace{typeof(float(first(r)))}, r)
833846

834847
# Promote FloatRange to LinSpace
835848
promote_rule{F,OR<:FloatRange}(::Type{LinSpace{F}}, ::Type{OR}) =
836849
LinSpace{promote_type(F,eltype(OR))}
837850
convert{T<:AbstractFloat}(::Type{LinSpace{T}}, r::FloatRange) =
838-
linspace(convert(T, first(r)), convert(T, last(r)), convert(T, length(r)))
851+
linspace(convert(T, first(r)), convert(T, last(r)), length(r))
839852
convert{T<:AbstractFloat}(::Type{LinSpace}, r::FloatRange{T}) =
840853
convert(LinSpace{T}, r)
841854

@@ -877,7 +890,7 @@ collect(r::Range) = vcat(r)
877890

878891
reverse(r::OrdinalRange) = colon(last(r), -step(r), first(r))
879892
reverse(r::FloatRange) = FloatRange(r.start + (r.len-1)*r.step, -r.step, r.len, r.divisor)
880-
reverse(r::LinSpace) = LinSpace(r.stop, r.start, r.len, r.divisor)
893+
reverse(r::LinSpace) = LinSpace(r.stop, r.start, length(r))
881894

882895
## sorting ##
883896

base/rational.jl

+4
Original file line numberDiff line numberDiff line change
@@ -394,3 +394,7 @@ end
394394
function ^{T<:Rational}(z::Complex{T}, n::Integer)
395395
n >= 0 ? power_by_squaring(z,n) : power_by_squaring(inv(z),-n)
396396
end
397+
398+
function lerpi(j::Integer, d::Integer, a::Rational, b::Rational)
399+
((d-j)*a)/d + (j*b)/d
400+
end

0 commit comments

Comments
 (0)