Repository navigation
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #362 +/- ##
==========================================
- Coverage 96.75% 96.74% -0.01%
==========================================
Files 7 7
Lines 800 799 -1
==========================================
- Hits 774 773 -1
Misses 26 26 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
PatrickHaecker
left a comment
There was a problem hiding this comment.
I think this would currently violate the promised interface, but the fix seems plausible. LGTM with the fix.
I still haven't understood why this is better than just doing a 0.10 release in the next days, but that is irrelevant in terms of correctness.
| floattype(::Type{X}) where {T <: ShortInts, X <: FixedPoint{T}} = Float32 | ||
| floattype(::Type{X}) where {T <: Integer, X <: FixedPoint{T}} = Float64 | ||
| floattype(::Type{X}) where {T <: LongInts, X <: FixedPoint{T}} = BigFloat | ||
| floattype(::Type{<:FixedPoint}) = BigFloat # fallback for `FixedPoint` |
There was a problem hiding this comment.
Your proposal would return BigFloat for Union{N0f8, N0f16}, which in my read violates the docstring: "In general the returned type should have the minimum bitwidth needed to encode the full precision of the input type."
Not perfect, but I think better:
| floattype(::Type{<:FixedPoint}) = BigFloat # fallback for `FixedPoint` | |
| floattype(::Type{T}) where {T <: FixedPoint} = | |
| T isa Union ? promote_type(map(floattype, Base.uniontypes(T))...) : BigFloat |
There was a problem hiding this comment.
As far as non-concrete types are concerned, I don't think that document makes much sense.
BTW, have you read this comment? #343 (comment)
As you've probably noticed, we can create various edge cases (e.g., Union{N0f8, Float32}).
| @test floattype(FixedPoint) === BigFloat | ||
| @test floattype(Normed) === BigFloat | ||
| @test floattype(Fixed) === BigFloat | ||
| @test floattype(FixedPoint{Int8}) === Float32 | ||
| @test floattype(Normed{UInt32}) === Float64 | ||
| @test floattype(Fixed{Int64}) === BigFloat | ||
| @test floattype(FixedPoint{MyInteger}) === Float64 | ||
| @test floattype(FixedPoint{Union{},1}) === BigFloat | ||
| @test floattype(Normed{Unsigned,2}) === BigFloat | ||
| @test floattype(Fixed{T,3} where {T<:Signed}) === BigFloat |
There was a problem hiding this comment.
Please add few non-trivial Union cases, too.
| """ | ||
| floattype(::Type{T})::Type{<:AbstractFloat} | ||
|
|
||
| Return a minimal type suitable for performing computations with instances of type `T` without integer overflow. | ||
|
|
||
| The fallback definition of `floattype(T)` applies only to `T<:AbstractFloat`. | ||
| However, it is permissible to extend `floattype` to return types that are not subtypes of | ||
| `AbstractFloat`; the key characteristic is that the return type should support computation without integer overflow. | ||
|
|
||
| In general the returned type should have the minimum bitwidth needed to encode the full precision of the input type. | ||
| however, a priority should be placed on computational efficiency; consequently, types like `Float16` should be avoided | ||
| except in scenarios where they are guaranteed to have hardware support. | ||
|
|
||
| # Examples | ||
|
|
||
| A classic usage is to avoid overflow behavior by promoting `FixedPoint` to `AbstractFloat` | ||
|
|
||
| ```jldoctest; setup = :(using FixedPointNumbers) | ||
| julia> x = N0f8(1.0) | ||
| 1.0N0f8 | ||
|
|
||
| julia> x + x # overflow | ||
| 0.996N0f8 | ||
|
|
||
| julia> T = floattype(x) | ||
| Float32 | ||
|
|
||
| julia> T(x) + T(x) | ||
| 2.0f0 | ||
| ``` | ||
|
|
||
| The following represents a valid extension of `floattype` to non-AbstractFloats: | ||
|
|
||
| ```julia | ||
| julia> using FixedPointNumbers, ColorTypes | ||
|
|
||
| julia> floattype(RGB{N0f8}) | ||
| RGB{Float32} | ||
| ``` | ||
|
|
||
| `RGB` itself is not a subtype of `AbstractFloat`, but unlike `RGB{N0f8}` operations with `RGB{Float32}` are not subject to integer overflow. | ||
| """ |
There was a problem hiding this comment.
The docstring had some weaknesses and should describe the new situation explicitly:
| """ | |
| floattype(::Type{T}) | |
| floattype(x::FixedPoint) | |
| Return a minimal type suitable for performing computations with instances of type `T` without integer overflow. | |
| For a `Real` type `T`, the returned type is a subtype of `AbstractFloat`. In general it has the minimum bitwidth | |
| needed to encode the full precision of `T`. However, computational efficiency takes priority, so the returned type | |
| may be wider. For example, `floattype(N0f8)` is currently `Float32` rather than `Float16`. | |
| For a non-concrete `FixedPoint` type, the returned type suits all of its concrete subtypes. A `Union` of `FixedPoint` | |
| types gives the promoted type of its members. Any other non-concrete `FixedPoint` type gives `floattype(R)` if its raw | |
| type `R` is concrete, and `BigFloat` otherwise. | |
| Methods are defined for `AbstractFloat`, `Integer`, `Rational`, `AbstractIrrational` and `FixedPoint` types. Other | |
| `Real` types currently return `T` itself with a deprecation warning, and will throw a `MethodError` in a future | |
| release. Packages may extend `floattype` to types that are not `Real`, as long as the returned type supports | |
| computation without integer overflow. | |
| # Examples | |
| A classic usage is to avoid overflow by converting `FixedPoint` values to a floating-point type: | |
| ```jldoctest; setup = :(using FixedPointNumbers) | |
| julia> x = N0f8(1.0) | |
| 1.0N0f8 | |
| julia> x + x # overflow | |
| 0.996N0f8 | |
| julia> T = floattype(x) | |
| Float32 | |
| julia> T(x) + T(x) | |
| 2.0f0 |
Non-concrete FixedPoint types:
julia> floattype(Normed{UInt8})
Float32
julia> floattype(Union{N0f8, N0f16})
Float32
julia> floattype(Normed)
BigFloat
The following represents a valid extension of floattype to types that are not subtypes of AbstractFloat:
julia> using FixedPointNumbers, ColorTypes
julia> floattype(RGB{N0f8})
RGB{Float32}RGB itself is not a subtype of AbstractFloat, but unlike RGB{N0f8}, operations with RGB{Float32} are not subject
to integer overflow.
"""
There was a problem hiding this comment.
Aside from the fact that it's long, I think the proposal isn't bad.
However, I don't understand exactly what purpose it serves.
This document is primarily intended for package developers. I don't know of any examples where floattype is actually being used outside of ColorTypes and its downstream packages.
I think the top-down definition of floattype should be discussed in JuliaLang/julia rather than in this package.
|
@PatrickHaecker |
|
@kimikage
The
julia> promote_type(Int128, Float16)
Float16That seems a bit extreme to me, but that is what we should go for if we want to be as compatible as possible to
julia> promote_type(Union{Int8, Int16}, Float32)
RealSo we probably don't want to strive for maximum w = vcat(Int8[100], Int32[3])
w[1] + w[1] # 200: the Int8 was converted to Int32
w = vcat(Int16[100], Int32[3])
w[1] + w[1] # 200: the Int16 was converted to Int32
a = Union{Int8, Int16}[Int8(100)]
v = vcat(a, Int32[3]) # eltype Signed, elements Int8, Int16, Int32
v[1] + v[1] # -56: Int8 arithmetic overflowsI reported that. Should we maybe see whether that report provides any insights? |
This is a variant of PR #354 with deprecated fallback method retained.
While it would be hypocritical not to call this a breaking change, it does allow us to avoid a direct violation of SemVer.
In other words, I plan to release this in the v0.9 series.