diff --git a/src/FixedPointNumbers.jl b/src/FixedPointNumbers.jl index 9e77efa2..b008f4be 100644 --- a/src/FixedPointNumbers.jl +++ b/src/FixedPointNumbers.jl @@ -23,6 +23,7 @@ export FixedPoint, Fixed, Normed, + floattype, # "special" typealiases # Q and U typealiases are exported in separate source files # Functions @@ -74,9 +75,38 @@ widen1(::Type{UInt128}) = UInt128 widen1(x::Integer) = x % widen1(typeof(x)) const ShortInts = Union{Int8,UInt8,Int16,UInt16} +const LongInts = Union{UInt64, UInt128, Int64, Int128, BigInt} +""" + floattype(::Type{T}) + +Return the minimum float type that represents `T` without overflow to `Inf`. + +# Example + +A classic usage is to avoid overflow behavior by promoting `FixedPoint` to `AbstractFloat` + +```julia +julia> x = N0f8(1.0) +1.0N0f8 + +julia> x + x # overflow +0.996N0f8 + +julia> float_x = floattype(eltype(x))(x) +1.0f0 + +julia> float_x + float_x +2.0f0 +``` +""" +floattype(::Type{T}) where {T <: Real} = T # fallback +floattype(::Type{T}) where {T <: Union{ShortInts, Bool}} = Float32 +floattype(::Type{T}) where {T <: Integer} = Float64 +floattype(::Type{T}) where {T <: LongInts} = BigFloat floattype(::Type{FixedPoint{T,f}}) where {T <: ShortInts,f} = Float32 floattype(::Type{FixedPoint{T,f}}) where {T <: Integer,f} = Float64 +floattype(::Type{FixedPoint{T,f}}) where {T <: LongInts,f} = BigFloat floattype(::Type{F}) where {F <: FixedPoint} = floattype(supertype(F)) floattype(x::FixedPoint) = floattype(typeof(x)) diff --git a/test/runtests.jl b/test/runtests.jl index 53f53ca6..570cec53 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -8,3 +8,7 @@ end @testset "fixed" begin include("fixed.jl") end + +@testset "traits" begin + include("traits.jl") +end diff --git a/test/traits.jl b/test/traits.jl new file mode 100644 index 00000000..874a47b4 --- /dev/null +++ b/test/traits.jl @@ -0,0 +1,19 @@ +@testset "floattype" begin + function _is_fixed_type(x::Symbol) + try + @eval $(x) isa Type && $(x) <: FixedPoint && return true + catch + return false + end + end + + fixed_types = setdiff(filter(_is_fixed_type, names(FixedPointNumbers)), [:Fixed, :Normed, :FixedPoint]) + fixed_types = [@eval $(x) for x in fixed_types] + + exact_types = vcat([UInt8, UInt16, UInt32, UInt64, UInt128, Bool, + Int8, Int16, Int32, Int64, Int128], + fixed_types) + for T in exact_types + @test typemax(T) <= maxintfloat(floattype(T)) + end +end