Skip to content

Commit bae6542

Browse files
authored
Fix range construction and length (#163)
1 parent 402e637 commit bae6542

File tree

6 files changed

+90
-5
lines changed

6 files changed

+90
-5
lines changed

src/FixedPointNumbers.jl

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import Base: ==, <, <=, -, +, *, /, ~, isapprox,
66
zero, oneunit, one, typemin, typemax, floatmin, floatmax, eps, sizeof, reinterpret,
77
float, trunc, round, floor, ceil, bswap,
88
div, fld, rem, mod, mod1, fld1, min, max, minmax,
9-
rand
9+
rand, length
10+
11+
using Base.Checked: checked_add, checked_sub, checked_div
1012

1113
using Base: @pure
1214

@@ -149,6 +151,19 @@ for (m, f) in ((:(:Nearest), :round),
149151
end
150152
end
151153

154+
function length(r::StepRange{X,X}) where {X <: FixedPoint{<:ShorterThanInt}}
155+
start, step, stop = Int(reinterpret(r.start)), Int(reinterpret(r.step)), Int(reinterpret(r.stop))
156+
return div((stop - start) + step, step)
157+
end
158+
function length(r::StepRange{X,X}) where {X <: FixedPoint}
159+
start, step, stop = reinterpret(r.start), reinterpret(r.step), reinterpret(r.stop)
160+
return checked_div(checked_add(checked_sub(stop, start), step), step)
161+
end
162+
function length(r::StepRange{<:FixedPoint})
163+
start, step, stop = float(r.start), r.step, float(r.stop)
164+
return div((stop - start) + step, step)
165+
end
166+
152167
# Printing. These are used to generate type-symbols, so we need them
153168
# before we include any files.
154169
function showtype(io::IO, ::Type{X}) where {X <: FixedPoint}

src/fixed.jl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,16 @@ function round(::Type{Ti}, x::Fixed{T,f}) where {Ti <: Integer, T, f}
161161
convert(Ti, z - Ti(y & m == rawone(x)))
162162
end
163163

164+
# Range construction
165+
Base.unitrange_last(start::F, stop::F) where {F<:Fixed} =
166+
stop >= start ? convert(F, start+floor(stop-start)) : convert(F, start+F(-1))
167+
168+
# Range lengths
169+
length(r::AbstractUnitRange{F}) where {F <: Fixed{<:SShorterThanInt,f}} where {f} =
170+
((Int(reinterpret(last(r))) - Int(reinterpret(first(r)))) >> f) + 1
171+
length(r::AbstractUnitRange{F}) where {F <: Fixed{T}} where {T <: Signed} =
172+
checked_add(checked_sub(floor(T, last(r)), floor(T, first(r))), oneunit(T))
173+
164174
promote_rule(ft::Type{Fixed{T,f}}, ::Type{TI}) where {T,f,TI <: Integer} = Fixed{T,f}
165175
promote_rule(::Type{Fixed{T,f}}, ::Type{TF}) where {T,f,TF <: AbstractFloat} = TF
166176
promote_rule(::Type{Fixed{T,f}}, ::Type{Rational{TR}}) where {T,f,TR} = Rational{TR}

src/normed.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,12 @@ function decompose(x::Normed)
287287
div(reinterpret(x),g), 0, div(rawone(x),g)
288288
end
289289

290+
# Range lengths
291+
length(r::AbstractUnitRange{N}) where {N <: Normed{<:UShorterThanInt}} =
292+
floor(Int, last(r)) - floor(Int, first(r)) + 1
293+
length(r::AbstractUnitRange{N}) where {N <: Normed{T}} where {T<:Unsigned} =
294+
r.start > r.stop ? T(0) : checked_add(floor(T, last(r)) - floor(T, first(r)), oneunit(T))
295+
290296
# Promotions
291297
promote_rule(::Type{T}, ::Type{Tf}) where {T <: Normed,Tf <: AbstractFloat} = promote_type(floattype(T), Tf)
292298
promote_rule(::Type{T}, ::Type{R}) where {T <: Normed,R <: Rational} = R

src/utilities.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ widen1(x::Integer) = x % widen1(typeof(x))
1616
const ShortInts = Union{Int8, UInt8, Int16, UInt16}
1717
const LongInts = Union{Int64, UInt64, Int128, UInt128, BigInt}
1818

19+
const ShorterThanInt = Int === Int32 ? ShortInts : Union{ShortInts, Int32, UInt32}
20+
const NotBiggerThanInt = Union{ShorterThanInt, Int, UInt}
21+
const SShorterThanInt = typeintersect(ShorterThanInt, Signed)
22+
const UShorterThanInt = typeintersect(ShorterThanInt, Unsigned)
23+
1924
macro f32(x::Float64) # just for hexadecimal floating-point literals
2025
:(Float32($x))
2126
end

test/fixed.jl

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,34 @@ end
183183
end
184184
end
185185

186+
@testset "unit range" begin
187+
@test length(Q1f6(-1):Q1f6(0)) == 2
188+
@test length(Q1f6(0):Q1f6(-1)) == 0
189+
@test collect(Q1f6(-1):Q1f6(0)) == Q1f6[-1, 0]
190+
@test length(Q6f1(-64):Q6f1(63)) == 128
191+
QIntW = Fixed{Int,bitwidth(Int)-1}
192+
@test length(QIntW(-1):QIntW(0)) == 2
193+
QInt1 = Fixed{Int,1}
194+
@test length(typemin(QInt1):typemax(QInt1)-oneunit(QInt1)) == typemax(Int)
195+
@test_throws OverflowError length(typemin(QInt1):typemax(QInt1))
196+
@test length(-127Q7f0:127Q7f0) == 255
197+
@test length(Q1f62(0):Q1f62(-2)) == 0
198+
end
199+
200+
@testset "step range" begin
201+
r = typemin(Q0f7):eps(Q0f7):typemax(Q0f7)
202+
counter = 0
203+
for x in r
204+
counter += 1
205+
end
206+
@test counter == 256
207+
@test length(r) == 256
208+
QInt1 = Fixed{Int,1}
209+
@test length(QInt1(0):eps(QInt1):typemax(QInt1)-eps(QInt1)) == typemax(Int)
210+
@test Base.unsafe_length(typemin(QInt1):eps(QInt1):typemax(QInt1)-eps(QInt1)) == -1
211+
@test_throws OverflowError length(QInt1(-1):eps(QInt1):typemax(QInt1)-eps(QInt1))
212+
end
213+
186214
@testset "reductions" begin
187215
a = Q0f7[0.75, 0.5]
188216
acmp = Float64(a[1]) + Float64(a[2])

test/normed.jl

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -300,17 +300,38 @@ end
300300
@test bswap(N0f8(0.5)) === N0f8(0.5)
301301
@test bswap(N0f16(0.5)) === reinterpret(N0f16, 0x0080)
302302
@test minmax(N0f8(0.8), N0f8(0.2)) === (N0f8(0.2), N0f8(0.8))
303+
end
303304

304-
r = reinterpret(N0f8, 0x01):reinterpret(N0f8, 0x01):reinterpret(N0f8, convert(UInt8, 48))
305-
@test length(r) == 48
306-
end
305+
@testset "unit range" begin
306+
@test length(N0f8(0):N0f8(1)) == 2
307+
@test length(N0f8(1):N0f8(0)) == 0
308+
@test isempty(N0f8(1):N0f8(0))
309+
@test collect(N0f8(0):N0f8(1)) == N0f8[0, 1]
310+
@test length(0.5N1f7:1.504N1f7) == 2
311+
@test length(N7f1(0):N7f1(255)) == 256
312+
NIntW = Normed{UInt,bitwidth(UInt)}
313+
@test length(NIntW(0):NIntW(1)) == 2
314+
NInt1 = Normed{UInt,1}
315+
@test length(NInt1(0):typemax(NInt1)-oneunit(NInt1)) == typemax(UInt)
316+
@test_throws OverflowError length(NInt1(0):typemax(NInt1))
317+
@test Base.unsafe_length(NInt1(0):typemax(NInt1)) == 0 # overflow
318+
N64f64 = Normed{UInt128,64}
319+
@test_broken length(N64f64(0):typemax(N64f64)) == UInt128(typemax(UInt64)) + 1
320+
@test length(N1f63(2):N1f63(0)) == 0
321+
end
307322

308-
@testset "step range" begin
323+
@testset "step range" begin
309324
counter = 0
310325
for x in N0f8(0):eps(N0f8):N0f8(1)
311326
counter += 1
312327
end
313328
@test counter == 256
329+
@test length(N0f8(0):eps(N0f8):N0f8(1)) == 256
330+
r = reinterpret(N0f8, 0x01):reinterpret(N0f8, 0x01):reinterpret(N0f8, UInt8(48))
331+
@test length(r) == 48
332+
NInt1 = Normed{UInt,1}
333+
@test length(NInt1(0):NInt1(1):typemax(NInt1)-oneunit(NInt1)) == typemax(UInt)
334+
@test_throws OverflowError length(NInt1(0):NInt1(1):typemax(NInt1))
314335
end
315336

316337
@testset "Promotion within Normed" begin

0 commit comments

Comments
 (0)