diff --git a/src/FixedPointNumbers.jl b/src/FixedPointNumbers.jl index 8dea702b..f59013a5 100644 --- a/src/FixedPointNumbers.jl +++ b/src/FixedPointNumbers.jl @@ -6,7 +6,9 @@ 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 using Base: @pure @@ -149,6 +151,19 @@ for (m, f) in ((:(:Nearest), :round), end end +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 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 +function length(r::StepRange{<:FixedPoint}) + start, step, stop = float(r.start), r.step, float(r.stop) + return div((stop - start) + step, step) +end + # 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/src/fixed.jl b/src/fixed.jl index 38c9af92..a4c9b65f 100644 --- a/src/fixed.jl +++ b/src/fixed.jl @@ -161,6 +161,16 @@ 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 +length(r::AbstractUnitRange{F}) where {F <: Fixed{<:SShorterThanInt,f}} where {f} = + ((Int(reinterpret(last(r))) - Int(reinterpret(first(r)))) >> f) + 1 +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} 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..4b89d3e3 100644 --- a/src/normed.jl +++ b/src/normed.jl @@ -287,6 +287,12 @@ function decompose(x::Normed) div(reinterpret(x),g), 0, div(rawone(x),g) end +# Range lengths +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) 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..e0038088 100644 --- a/test/fixed.jl +++ b/test/fixed.jl @@ -183,6 +183,34 @@ end end 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} + @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)) + @test length(-127Q7f0:127Q7f0) == 255 + @test length(Q1f62(0):Q1f62(-2)) == 0 +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 c73b9c8e..80c5949c 100644 --- a/test/normed.jl +++ b/test/normed.jl @@ -300,17 +300,38 @@ 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 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 + 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 + @test length(N1f63(2):N1f63(0)) == 0 +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, 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