diff --git a/src/FixedPointNumbers.jl b/src/FixedPointNumbers.jl index 60560314..7f06c5d7 100644 --- a/src/FixedPointNumbers.jl +++ b/src/FixedPointNumbers.jl @@ -311,6 +311,35 @@ include("normed.jl") include("deprecations.jl") const UF = (N0f8, N6f10, N4f12, N2f14, N0f16) +# Promotions +promote_rule(::Type{X}, ::Type{Tf}) where {X <: FixedPoint, Tf <: AbstractFloat} = + promote_type(floattype(X), Tf) + +# Note that `Tr` does not always have enough domains. +promote_rule(::Type{X}, ::Type{Tr}) where {X <: FixedPoint, Tr <: Rational} = Tr + +promote_rule(::Type{X}, ::Type{Ti}) where {X <: FixedPoint, Ti <: Integer} = floattype(X) + +function promote_rule(::Type{X1}, ::Type{X2}) where {T1, f1, X1 <: FixedPoint{T1,f1}, + T2, f2, X2 <: FixedPoint{T2,f2}} + X = wrapper(X1) + X !== wrapper(X2) && return promote_type(floattype(X1), floattype(X2)) + + f = max(f1, f2) # ensure we have enough precision + Tp = promote_type(T1, T2) + T = (T1 <: Signed || T2 <: Signed) ? signedtype(Tp) : Tp + # make sure we have enough integer bits + m = max(nbitsint(X1), nbitsint(X2)) + _widen_rawtype(X{T,f}, m) +end + +function _widen_rawtype(::Type{X}, m) where {T, f, X<:FixedPoint{T,f}} + nbitsint(X) >= m && return X + Tw = widen1(T) + T === Tw && return X + _widen_rawtype(wrapper(X){Tw,f}, m) +end + # Promotions for reductions const Treduce = Float64 Base.add_sum(x::FixedPoint, y::FixedPoint) = Treduce(x) + Treduce(y) diff --git a/src/fixed.jl b/src/fixed.jl index 4596b34a..e2082814 100644 --- a/src/fixed.jl +++ b/src/fixed.jl @@ -195,24 +195,5 @@ length(r::AbstractUnitRange{F}) where {F <: Fixed{<:SShorterThanInt,f}} where {f 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} - -@generated function promote_rule(::Type{Fixed{T1,f1}}, ::Type{Fixed{T2,f2}}) where {T1,T2,f1,f2} - f = max(f1, f2) # ensure we have enough precision - T = promote_type(T1, T2) - # make sure we have enough integer bits - i1, i2 = bitwidth(T1)-f1, bitwidth(T2)-f2 # number of integer bits for each - i = bitwidth(T)-f - while i < max(i1, i2) - Tw = widen1(T) - T == Tw && break - T = Tw - i = bitwidth(T)-f - end - :(Fixed{$T,$f}) -end - # TODO: Document and check that it still does the right thing. decompose(x::Fixed{T,f}) where {T,f} = x.i, -f, 1 diff --git a/src/normed.jl b/src/normed.jl index 5c2f6c4f..83188107 100644 --- a/src/normed.jl +++ b/src/normed.jl @@ -303,24 +303,3 @@ 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 -function promote_rule(::Type{T}, ::Type{Ti}) where {T <: Normed,Ti <: Union{Signed, Unsigned}} - floattype(T) -end -@generated function promote_rule(::Type{Normed{T1,f1}}, ::Type{Normed{T2,f2}}) where {T1,T2,f1,f2} - f = max(f1, f2) # ensure we have enough precision - T = promote_type(T1, T2) - # make sure we have enough integer bits - i1, i2 = bitwidth(T1)-f1, bitwidth(T2)-f2 # number of integer bits for each - i = bitwidth(T)-f - while i < max(i1, i2) - Tw = widen1(T) - T == Tw && break - T = Tw - i = bitwidth(T)-f - end - :(Normed{$T,$f}) -end diff --git a/src/utilities.jl b/src/utilities.jl index 0976dd08..618ce295 100644 --- a/src/utilities.jl +++ b/src/utilities.jl @@ -1,6 +1,7 @@ # utility functions and macros, which are independent of `FixedPoint` bitwidth(T::Type) = 8sizeof(T) +widen1(T::Type) = T # fallback widen1(::Type{Int8}) = Int16 widen1(::Type{UInt8}) = UInt16 widen1(::Type{Int16}) = Int32 @@ -9,8 +10,6 @@ widen1(::Type{Int32}) = Int64 widen1(::Type{UInt32}) = UInt64 widen1(::Type{Int64}) = Int128 widen1(::Type{UInt64}) = UInt128 -widen1(::Type{Int128}) = Int128 -widen1(::Type{UInt128}) = UInt128 widen1(x::Integer) = x % widen1(typeof(x)) signedtype(::Type{T}) where {T <: Integer} = typeof(signed(zero(T))) @@ -48,3 +47,5 @@ if !signbit(signed(unsafe_trunc(UInt, -12.345))) # exclude BigFloat (issue #202) _unsafe_trunc(::Type{T}, x::BigFloat) where {T <: Integer} = unsafe_trunc(T, x) end + +wrapper(@nospecialize(T)) = Base.typename(T).wrapper diff --git a/test/fixed.jl b/test/fixed.jl index f11740e4..451a36ac 100644 --- a/test/fixed.jl +++ b/test/fixed.jl @@ -455,13 +455,24 @@ end @test Fixed{Int16,3}(-1) == Fixed{Int8,5}(-1) @test Fixed{Int16,3}(0.25) == Fixed{Int8,5}(0.25) - @test promote_type(Q0f7,Float32,Int) == Float32 - @test promote_type(Q0f7,Int,Float32) == Float32 - @test promote_type(Int,Q0f7,Float32) == Float32 - @test promote_type(Int,Float32,Q0f7) == Float32 - @test promote_type(Float32,Int,Q0f7) == Float32 - @test promote_type(Float32,Q0f7,Int) == Float32 - @test promote_type(Q0f7,Q1f6,Q2f5,Q3f4,Q4f3,Q5f2) == Fixed{Int128,7} + @test @inferred(promote_type(Q0f7, Float64)) === Float64 + @test @inferred(promote_type(Float32, Q7f24)) === Float64 + + @test @inferred(promote_type(Q0f7, Int8)) === Float32 + @test @inferred(promote_type(Int128, Q7f24)) === Float64 + + @test @inferred(promote_type(Q0f15, Rational{UInt8})) === Rational{UInt8} + + @test @inferred(promote_type(Q0f7, Float32, Int)) === Float32 + @test @inferred(promote_type(Q0f7, Int, Float32)) === Float32 + @test @inferred(promote_type(Int, Q0f7, Float32)) === Float32 + @test @inferred(promote_type(Int, Float32, Q0f7)) === Float32 + @test @inferred(promote_type(Float32, Int, Q0f7)) === Float32 + @test @inferred(promote_type(Float32, Q0f7, Int)) === Float32 + + @test @inferred(promote_type(Q0f7,Q1f6,Q2f5,Q3f4,Q4f3,Q5f2)) == Fixed{Int128,7} + + @test @inferred(promote_type(Q0f7, N0f32)) === Float64 end @testset "show" begin diff --git a/test/normed.jl b/test/normed.jl index 0a6747b4..ff170434 100644 --- a/test/normed.jl +++ b/test/normed.jl @@ -485,13 +485,24 @@ end @test Normed{UInt16,4}(1) == Normed{UInt8,6}(1) @test Normed{UInt16,4}(0.2) == Normed{UInt8,6}(0.2) - @test promote_type(N0f8,Float32,Int) == Float32 - @test promote_type(N0f8,Int,Float32) == Float32 - @test promote_type(Int,N0f8,Float32) == Float32 - @test promote_type(Int,Float32,N0f8) == Float32 - @test promote_type(Float32,Int,N0f8) == Float32 - @test promote_type(Float32,N0f8,Int) == Float32 - @test promote_type(N0f8,N1f7,N2f6,N3f5,N4f4,N5f3) == Normed{UInt128,8} + @test @inferred(promote_type(N0f8, Float64)) === Float64 + @test @inferred(promote_type(Float32, N8f24)) === Float64 + + @test @inferred(promote_type(N0f8, Int8)) === Float32 + @test @inferred(promote_type(Int128, N8f24)) === Float64 + + @test @inferred(promote_type(N0f16, Rational{Int8})) === Rational{Int8} + + @test @inferred(promote_type(N0f8, Float32, Int)) === Float32 + @test @inferred(promote_type(N0f8, Int, Float32)) === Float32 + @test @inferred(promote_type(Int, N0f8, Float32)) === Float32 + @test @inferred(promote_type(Int, Float32, N0f8)) === Float32 + @test @inferred(promote_type(Float32, Int, N0f8)) === Float32 + @test @inferred(promote_type(Float32, N0f8, Int)) === Float32 + + @test @inferred(promote_type(N0f8,N1f7,N2f6,N3f5,N4f4,N5f3)) === Normed{UInt128,8} + + @test @inferred(promote_type(N0f8, Q0f31)) === Float64 end @testset "show" begin