From 12c1ea58ce3352a4701748cdf5fa66bf6eafd368 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Sat, 11 Jan 2020 06:14:06 -0600 Subject: [PATCH 1/3] Fix UnitRange construction and `length(r)` --- src/FixedPointNumbers.jl | 2 ++ src/fixed.jl | 14 ++++++++++++++ src/normed.jl | 10 ++++++++++ src/utilities.jl | 5 +++++ test/fixed.jl | 11 +++++++++++ test/normed.jl | 22 ++++++++++++++++++---- 6 files changed, 60 insertions(+), 4 deletions(-) diff --git a/src/FixedPointNumbers.jl b/src/FixedPointNumbers.jl index 8dea702b..f4ada7c9 100644 --- a/src/FixedPointNumbers.jl +++ b/src/FixedPointNumbers.jl @@ -8,6 +8,8 @@ import Base: ==, <, <=, -, +, *, /, ~, isapprox, div, fld, rem, mod, mod1, fld1, min, max, minmax, rand +using Base.Checked: checked_add, checked_sub + using Base: @pure """ diff --git a/src/fixed.jl b/src/fixed.jl index 38c9af92..1615d76f 100644 --- a/src/fixed.jl +++ b/src/fixed.jl @@ -161,6 +161,20 @@ function round(::Type{Ti}, x::Fixed{T,f}) where {Ti <: Integer, T, f} convert(Ti, z - Ti(y & m == rawone(x))) end +# Range construction +Base.unitrange_last(start::F, stop::F) where {F<:Fixed} = + stop >= start ? convert(F, start+floor(stop-start)) : convert(F, start+F(-1)) + +# Range lengths +Base.unsafe_length(r::AbstractUnitRange{F}) where {F <: Fixed{<:SShorterThanInt,f}} where {f} = + ((Int(reinterpret(last(r))) - Int(reinterpret(first(r)))) >> f) + 1 +Base.unsafe_length(r::AbstractUnitRange{F}) where {F <: Fixed{<:Signed}} = + (floor(T, last(r)) - floor(T, first(r))) + oneunit(T) +Base.length(r::AbstractUnitRange{F}) where {F <: Fixed{<:SShorterThanInt}} = + Base.unsafe_length(r) +Base.length(r::AbstractUnitRange{F}) where {F <: Fixed{<:Signed}} = + checked_add(checked_sub(floor(T, last(r)), floor(T, first(r))), oneunit(T)) + promote_rule(ft::Type{Fixed{T,f}}, ::Type{TI}) where {T,f,TI <: Integer} = Fixed{T,f} promote_rule(::Type{Fixed{T,f}}, ::Type{TF}) where {T,f,TF <: AbstractFloat} = TF promote_rule(::Type{Fixed{T,f}}, ::Type{Rational{TR}}) where {T,f,TR} = Rational{TR} diff --git a/src/normed.jl b/src/normed.jl index b359059e..89d4ec23 100644 --- a/src/normed.jl +++ b/src/normed.jl @@ -287,6 +287,16 @@ function decompose(x::Normed) div(reinterpret(x),g), 0, div(rawone(x),g) end +# Range lengths +Base.unsafe_length(r::AbstractUnitRange{N}) where {N <: Normed{<:UShorterThanInt}} = + floor(Int, last(r) - first(r)) + 1 +Base.unsafe_length(r::AbstractUnitRange{N}) where {N <: Normed{T,f}} where {T<:Unsigned,f} = + floor(T, last(r) - first(r)) + oneunit(T) +Base.length(r::AbstractUnitRange{N}) where {N <: Normed{<:UShorterThanInt}} = + Base.unsafe_length(r) +Base.length(r::AbstractUnitRange{N}) where {N <: Normed{T}} where {T<:Unsigned} = + checked_add(floor(T, last(r) - first(r)), oneunit(T)) + # Promotions promote_rule(::Type{T}, ::Type{Tf}) where {T <: Normed,Tf <: AbstractFloat} = promote_type(floattype(T), Tf) promote_rule(::Type{T}, ::Type{R}) where {T <: Normed,R <: Rational} = R diff --git a/src/utilities.jl b/src/utilities.jl index 5bc17388..8d1b84b5 100644 --- a/src/utilities.jl +++ b/src/utilities.jl @@ -16,6 +16,11 @@ widen1(x::Integer) = x % widen1(typeof(x)) const ShortInts = Union{Int8, UInt8, Int16, UInt16} const LongInts = Union{Int64, UInt64, Int128, UInt128, BigInt} +const ShorterThanInt = Int === Int32 ? ShortInts : Union{ShortInts, Int32, UInt32} +const NotBiggerThanInt = Union{ShorterThanInt, Int, UInt} +const SShorterThanInt = typeintersect(ShorterThanInt, Signed) +const UShorterThanInt = typeintersect(ShorterThanInt, Unsigned) + macro f32(x::Float64) # just for hexadecimal floating-point literals :(Float32($x)) end diff --git a/test/fixed.jl b/test/fixed.jl index 13b6a79d..dbf0d3b0 100644 --- a/test/fixed.jl +++ b/test/fixed.jl @@ -183,6 +183,17 @@ end end end +@testset "unit range" begin + @test length(Q1f6(-1):Q1f6(0)) == 2 + @test collect(Q1f6(-1):Q1f6(0)) == Q1f6[-1, 0] + @test length(Q6f1(-64):Q6f1(63)) == 128 + QIntW = Fixed{Int,bitwidth(Int)-1} + @test length(QIntW(-1):QIntW(0)) == 2 + QInt1 = Fixed{Int,1} + @test length(typemin(QInt1):typemax(QInt1)-oneunit(QInt1)) == typemax(Int) + @test_throws OverflowError length(typemin(QInt1):typemax(QInt1)) +end + @testset "reductions" begin a = Q0f7[0.75, 0.5] acmp = Float64(a[1]) + Float64(a[2]) diff --git a/test/normed.jl b/test/normed.jl index c73b9c8e..549f95de 100644 --- a/test/normed.jl +++ b/test/normed.jl @@ -300,17 +300,31 @@ end @test bswap(N0f8(0.5)) === N0f8(0.5) @test bswap(N0f16(0.5)) === reinterpret(N0f16, 0x0080) @test minmax(N0f8(0.8), N0f8(0.2)) === (N0f8(0.2), N0f8(0.8)) +end - r = reinterpret(N0f8, 0x01):reinterpret(N0f8, 0x01):reinterpret(N0f8, convert(UInt8, 48)) - @test length(r) == 48 - end +@testset "unit range" begin + @test length(N0f8(0):N0f8(1)) == 2 + @test collect(N0f8(0):N0f8(1)) == N0f8[0, 1] + @test length(N7f1(0):N7f1(255)) == 256 + NIntW = Normed{UInt,bitwidth(UInt)} + @test length(NIntW(0):NIntW(1)) == 2 + NInt1 = Normed{UInt,1} + @test length(NInt1(0):typemax(NInt1)-oneunit(NInt1)) == typemax(UInt) + @test_throws OverflowError length(NInt1(0):typemax(NInt1)) + @test Base.unsafe_length(NInt1(0):typemax(NInt1)) == 0 # overflow + N64f64 = Normed{UInt128,64} + @test_broken length(N64f64(0):typemax(N64f64)) == UInt128(typemax(UInt64)) + 1 +end - @testset "step range" begin +@testset "step range" begin counter = 0 for x in N0f8(0):eps(N0f8):N0f8(1) counter += 1 end @test counter == 256 + @test length(N0f8(0):eps(N0f8):N0f8(1)) == 256 + r = reinterpret(N0f8, 0x01):reinterpret(N0f8, 0x01):reinterpret(N0f8, convert(UInt8, 48)) + @test length(r) == 48 end @testset "Promotion within Normed" begin From ef7aeb8cca7170c08e9267848807db4bfebf8c19 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Sat, 11 Jan 2020 06:38:40 -0600 Subject: [PATCH 2/3] Fix `length` for StepRange --- src/FixedPointNumbers.jl | 22 +++++++++++++++++++++- test/fixed.jl | 14 ++++++++++++++ test/normed.jl | 5 ++++- 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/FixedPointNumbers.jl b/src/FixedPointNumbers.jl index f4ada7c9..2b4dfe0c 100644 --- a/src/FixedPointNumbers.jl +++ b/src/FixedPointNumbers.jl @@ -8,7 +8,7 @@ import Base: ==, <, <=, -, +, *, /, ~, isapprox, div, fld, rem, mod, mod1, fld1, min, max, minmax, rand -using Base.Checked: checked_add, checked_sub +using Base.Checked: checked_add, checked_sub, checked_div using Base: @pure @@ -151,6 +151,26 @@ for (m, f) in ((:(:Nearest), :round), end end +function Base.unsafe_length(r::StepRange{X,X}) where {X <: FixedPoint{<:ShorterThanInt}} + start, step, stop = reinterpret(r.start), reinterpret(r.step), reinterpret(r.stop) + return div(Int(stop) - Int(start) + Int(step), Int(step)) +end +function Base.unsafe_length(r::StepRange{X,X}) where {X <: FixedPoint} + start, step, stop = reinterpret(r.start), reinterpret(r.step), reinterpret(r.stop) + return div((stop - start) + step, step) +end +function Base.unsafe_length(r::StepRange{<:FixedPoint}) + start, step, stop = float(r.start), r.step, float(r.stop) + return div((stop - start) + step, step) +end +Base.length(r::StepRange{X,X}) where {X <: FixedPoint{<:ShorterThanInt}} = + Base.unsafe_length(r) +function Base.length(r::StepRange{X,X}) where {X <: FixedPoint} + start, step, stop = reinterpret(r.start), reinterpret(r.step), reinterpret(r.stop) + return checked_div(checked_add(checked_sub(stop, start), step), step) +end +Base.length(r::StepRange{<:FixedPoint}) = Base.unsafe_length(r) + # Printing. These are used to generate type-symbols, so we need them # before we include any files. function showtype(io::IO, ::Type{X}) where {X <: FixedPoint} diff --git a/test/fixed.jl b/test/fixed.jl index dbf0d3b0..cd47389e 100644 --- a/test/fixed.jl +++ b/test/fixed.jl @@ -194,6 +194,20 @@ end @test_throws OverflowError length(typemin(QInt1):typemax(QInt1)) end +@testset "step range" begin + r = typemin(Q0f7):eps(Q0f7):typemax(Q0f7) + counter = 0 + for x in r + counter += 1 + end + @test counter == 256 + @test length(r) == 256 + QInt1 = Fixed{Int,1} + @test length(QInt1(0):eps(QInt1):typemax(QInt1)-eps(QInt1)) == typemax(Int) + @test Base.unsafe_length(typemin(QInt1):eps(QInt1):typemax(QInt1)-eps(QInt1)) == -1 + @test_throws OverflowError length(QInt1(-1):eps(QInt1):typemax(QInt1)-eps(QInt1)) +end + @testset "reductions" begin a = Q0f7[0.75, 0.5] acmp = Float64(a[1]) + Float64(a[2]) diff --git a/test/normed.jl b/test/normed.jl index 549f95de..c2178b88 100644 --- a/test/normed.jl +++ b/test/normed.jl @@ -323,8 +323,11 @@ end end @test counter == 256 @test length(N0f8(0):eps(N0f8):N0f8(1)) == 256 - r = reinterpret(N0f8, 0x01):reinterpret(N0f8, 0x01):reinterpret(N0f8, convert(UInt8, 48)) + r = reinterpret(N0f8, 0x01):reinterpret(N0f8, 0x01):reinterpret(N0f8, UInt8(48)) @test length(r) == 48 + NInt1 = Normed{UInt,1} + @test length(NInt1(0):NInt1(1):typemax(NInt1)-oneunit(NInt1)) == typemax(UInt) + @test_throws OverflowError length(NInt1(0):NInt1(1):typemax(NInt1)) end @testset "Promotion within Normed" begin From 991647cb23d869a725070d04983069aef3288276 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Mon, 13 Jan 2020 16:48:13 -0600 Subject: [PATCH 3/3] Implement only `length` --- src/FixedPointNumbers.jl | 21 +++++++-------------- src/fixed.jl | 8 ++------ src/normed.jl | 12 ++++-------- test/fixed.jl | 3 +++ test/normed.jl | 4 ++++ 5 files changed, 20 insertions(+), 28 deletions(-) diff --git a/src/FixedPointNumbers.jl b/src/FixedPointNumbers.jl index 2b4dfe0c..f59013a5 100644 --- a/src/FixedPointNumbers.jl +++ b/src/FixedPointNumbers.jl @@ -6,7 +6,7 @@ import Base: ==, <, <=, -, +, *, /, ~, isapprox, zero, oneunit, one, typemin, typemax, floatmin, floatmax, eps, sizeof, reinterpret, float, trunc, round, floor, ceil, bswap, div, fld, rem, mod, mod1, fld1, min, max, minmax, - rand + rand, length using Base.Checked: checked_add, checked_sub, checked_div @@ -151,25 +151,18 @@ for (m, f) in ((:(:Nearest), :round), end end -function Base.unsafe_length(r::StepRange{X,X}) where {X <: FixedPoint{<:ShorterThanInt}} - start, step, stop = reinterpret(r.start), reinterpret(r.step), reinterpret(r.stop) - return div(Int(stop) - Int(start) + Int(step), Int(step)) +function length(r::StepRange{X,X}) where {X <: FixedPoint{<:ShorterThanInt}} + start, step, stop = Int(reinterpret(r.start)), Int(reinterpret(r.step)), Int(reinterpret(r.stop)) + return div((stop - start) + step, step) end -function Base.unsafe_length(r::StepRange{X,X}) where {X <: FixedPoint} +function length(r::StepRange{X,X}) where {X <: FixedPoint} start, step, stop = reinterpret(r.start), reinterpret(r.step), reinterpret(r.stop) - return div((stop - start) + step, step) + return checked_div(checked_add(checked_sub(stop, start), step), step) end -function Base.unsafe_length(r::StepRange{<:FixedPoint}) +function length(r::StepRange{<:FixedPoint}) start, step, stop = float(r.start), r.step, float(r.stop) return div((stop - start) + step, step) end -Base.length(r::StepRange{X,X}) where {X <: FixedPoint{<:ShorterThanInt}} = - Base.unsafe_length(r) -function Base.length(r::StepRange{X,X}) where {X <: FixedPoint} - start, step, stop = reinterpret(r.start), reinterpret(r.step), reinterpret(r.stop) - return checked_div(checked_add(checked_sub(stop, start), step), step) -end -Base.length(r::StepRange{<:FixedPoint}) = Base.unsafe_length(r) # Printing. These are used to generate type-symbols, so we need them # before we include any files. diff --git a/src/fixed.jl b/src/fixed.jl index 1615d76f..a4c9b65f 100644 --- a/src/fixed.jl +++ b/src/fixed.jl @@ -166,13 +166,9 @@ Base.unitrange_last(start::F, stop::F) where {F<:Fixed} = stop >= start ? convert(F, start+floor(stop-start)) : convert(F, start+F(-1)) # Range lengths -Base.unsafe_length(r::AbstractUnitRange{F}) where {F <: Fixed{<:SShorterThanInt,f}} where {f} = +length(r::AbstractUnitRange{F}) where {F <: Fixed{<:SShorterThanInt,f}} where {f} = ((Int(reinterpret(last(r))) - Int(reinterpret(first(r)))) >> f) + 1 -Base.unsafe_length(r::AbstractUnitRange{F}) where {F <: Fixed{<:Signed}} = - (floor(T, last(r)) - floor(T, first(r))) + oneunit(T) -Base.length(r::AbstractUnitRange{F}) where {F <: Fixed{<:SShorterThanInt}} = - Base.unsafe_length(r) -Base.length(r::AbstractUnitRange{F}) where {F <: Fixed{<:Signed}} = +length(r::AbstractUnitRange{F}) where {F <: Fixed{T}} where {T <: Signed} = checked_add(checked_sub(floor(T, last(r)), floor(T, first(r))), oneunit(T)) promote_rule(ft::Type{Fixed{T,f}}, ::Type{TI}) where {T,f,TI <: Integer} = Fixed{T,f} diff --git a/src/normed.jl b/src/normed.jl index 89d4ec23..4b89d3e3 100644 --- a/src/normed.jl +++ b/src/normed.jl @@ -288,14 +288,10 @@ function decompose(x::Normed) end # Range lengths -Base.unsafe_length(r::AbstractUnitRange{N}) where {N <: Normed{<:UShorterThanInt}} = - floor(Int, last(r) - first(r)) + 1 -Base.unsafe_length(r::AbstractUnitRange{N}) where {N <: Normed{T,f}} where {T<:Unsigned,f} = - floor(T, last(r) - first(r)) + oneunit(T) -Base.length(r::AbstractUnitRange{N}) where {N <: Normed{<:UShorterThanInt}} = - Base.unsafe_length(r) -Base.length(r::AbstractUnitRange{N}) where {N <: Normed{T}} where {T<:Unsigned} = - checked_add(floor(T, last(r) - first(r)), oneunit(T)) +length(r::AbstractUnitRange{N}) where {N <: Normed{<:UShorterThanInt}} = + floor(Int, last(r)) - floor(Int, first(r)) + 1 +length(r::AbstractUnitRange{N}) where {N <: Normed{T}} where {T<:Unsigned} = + r.start > r.stop ? T(0) : checked_add(floor(T, last(r)) - floor(T, first(r)), oneunit(T)) # Promotions promote_rule(::Type{T}, ::Type{Tf}) where {T <: Normed,Tf <: AbstractFloat} = promote_type(floattype(T), Tf) diff --git a/test/fixed.jl b/test/fixed.jl index cd47389e..e0038088 100644 --- a/test/fixed.jl +++ b/test/fixed.jl @@ -185,6 +185,7 @@ end @testset "unit range" begin @test length(Q1f6(-1):Q1f6(0)) == 2 + @test length(Q1f6(0):Q1f6(-1)) == 0 @test collect(Q1f6(-1):Q1f6(0)) == Q1f6[-1, 0] @test length(Q6f1(-64):Q6f1(63)) == 128 QIntW = Fixed{Int,bitwidth(Int)-1} @@ -192,6 +193,8 @@ end QInt1 = Fixed{Int,1} @test length(typemin(QInt1):typemax(QInt1)-oneunit(QInt1)) == typemax(Int) @test_throws OverflowError length(typemin(QInt1):typemax(QInt1)) + @test length(-127Q7f0:127Q7f0) == 255 + @test length(Q1f62(0):Q1f62(-2)) == 0 end @testset "step range" begin diff --git a/test/normed.jl b/test/normed.jl index c2178b88..80c5949c 100644 --- a/test/normed.jl +++ b/test/normed.jl @@ -304,7 +304,10 @@ end @testset "unit range" begin @test length(N0f8(0):N0f8(1)) == 2 + @test length(N0f8(1):N0f8(0)) == 0 + @test isempty(N0f8(1):N0f8(0)) @test collect(N0f8(0):N0f8(1)) == N0f8[0, 1] + @test length(0.5N1f7:1.504N1f7) == 2 @test length(N7f1(0):N7f1(255)) == 256 NIntW = Normed{UInt,bitwidth(UInt)} @test length(NIntW(0):NIntW(1)) == 2 @@ -314,6 +317,7 @@ end @test Base.unsafe_length(NInt1(0):typemax(NInt1)) == 0 # overflow N64f64 = Normed{UInt128,64} @test_broken length(N64f64(0):typemax(N64f64)) == UInt128(typemax(UInt64)) + 1 + @test length(N1f63(2):N1f63(0)) == 0 end @testset "step range" begin