-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Base: TwicePrecision: improve constructors #49616
Conversation
FTR I don't run the tests locally because |
|
b4e04c5
to
0ebd6e1
Compare
0ebd6e1
to
931499c
Compare
931499c
to
ca50369
Compare
I think the idea of this PR is good (leaving fields uninitialized is generally bad), but if I'm not mistaken, existing calls with a single argument were already zero initialized with this, right? TwicePrecision{T}(x::T) where {T} = TwicePrecision{T}(x, zero(T)) Adding more specialized constructors for creating a |
This PR is not about uninitialized fields, I don't think there are any issues with uninitialized fields with
Essentially I removed bad constructors and slightly modified some existing ones and then rearranged the code. I don't think I actually added any new logic (except for dispatch).
As stated in the comment above them, the motivation for doing it like that is to reuse code with complex.jl. In case it's not clear why I modified complex.jl at all: my wish was to increase safety with making it less likely that a nonsensical |
About safety: the thing with multi-word floating-point, including double-word floating-point that's used here, is that the allowed element types have to be whitelisted. My goal (with this PR and later #49569) is to have a double-word implementation for |
I think the only solution is to make packages like Unitful not use Does this seem acceptable? |
Apologies, I misread
I don't follow - can't For example, I think having the specialization
I see - the current changes would not have achieved that goal: julia> Int <: Number
true
julia> Rational <: Number
true
That sounds more like a case for specializing the interactions between
I can't comment on what Unitful should or should not do - I think the current situation arises due to the extreme genericity of our ranges. Keeping that around/working by adding specializations for types where we know we can do better (like |
Correctness is improved for constructing `TwicePrecision{T}` from `TwicePrecision{S}`. Previously a call like `TwicePrecision{Float64}(::TwicePrecision{Float32})` would leave `hi` and `lo` unnormalized; while a call like `TwicePrecision{Float32}(::TwicePrecision{Float64})` would additionally introduce unnecessary truncation. Accuracy is improved for constructing `TwicePrecision` from types like `BigFloat` or `Rational`. Previously a call like `TwicePrecision{Float64}(::BigFloat)` would unconditionally leave `lo` as zero. Example code that tests the improvements (all Boolean values should evaluate to `true`): ```julia const TwicePrecision = Base.TwicePrecision const canonicalize2 = Base.canonicalize2 function twiceprecision_roundtrip_is_not_lossy( ::Type{S}, x::T, ) where {S<:Number, T<:Union{Number,TwicePrecision}} tw = TwicePrecision{S}(x) x == T(tw) end function twiceprecision_is_normalized(tw::Tw) where {Tw<:TwicePrecision} (hi, lo) = (tw.hi, tw.lo) normalized = Tw(canonicalize2(hi, lo)...) (abs(lo) ≤ abs(hi)) & (tw == normalized) end rand_twiceprecision(::Type{T}) where {T<:Number} = TwicePrecision{T}(rand(widen(T))) rand_twiceprecision_is_ok(::Type{T}) where {T<:Number} = !iszero(rand_twiceprecision(T).lo) setprecision(BigFloat, 70) # The mantissa needs to be just a bit larger than for `Float64` rand_twiceprecision_is_ok(Float32) rand_twiceprecision_is_ok(Float32) rand_twiceprecision_is_ok(Float32) rand_twiceprecision_is_ok(Float64) rand_twiceprecision_is_ok(Float64) rand_twiceprecision_is_ok(Float64) twiceprecision_roundtrip_is_not_lossy(Float64, rand(BigFloat)) twiceprecision_roundtrip_is_not_lossy(Float64, rand(BigFloat)) twiceprecision_roundtrip_is_not_lossy(Float64, rand(BigFloat)) twiceprecision_roundtrip_is_not_lossy(Float64, rand_twiceprecision(Float32)) twiceprecision_roundtrip_is_not_lossy(Float64, rand_twiceprecision(Float32)) twiceprecision_roundtrip_is_not_lossy(Float64, rand_twiceprecision(Float32)) twiceprecision_is_normalized(TwicePrecision{Float32}(rand_twiceprecision(Float64))) twiceprecision_is_normalized(TwicePrecision{Float32}(rand_twiceprecision(Float64))) twiceprecision_is_normalized(TwicePrecision{Float32}(rand_twiceprecision(Float64))) twiceprecision_is_normalized(TwicePrecision{Float64}(rand_twiceprecision(Float32))) twiceprecision_is_normalized(TwicePrecision{Float64}(rand_twiceprecision(Float32))) twiceprecision_is_normalized(TwicePrecision{Float64}(rand_twiceprecision(Float32))) ``` Updates JuliaLang#49589
ca50369
to
da7ddcf
Compare
I reduced the PR, leaving all the tricky issues for future PRs and making it more easily reviewable. |
Update the arithmetic code to use algorithms published since twiceprecision.jl got written. Handle complex numbers. Prevent harmful promotions. Some of the introduced extended precision arithmetic code will also be used in `base/div.jl` to tackle issue JuliaLang#49450 with PR JuliaLang#49561. TODO: evaluate the accuracy improvements and write tests after JuliaLang#49616 gets merged Results: The example in JuliaLang#33677 still gives the same result. I wasn't able to evaluate JuliaLang#23497 because of how much Julia changed in the meantime. TODO: check whether there are improvements for JuliaLang#26553 Updates JuliaLang#49589
Update the arithmetic code to use algorithms published since twiceprecision.jl got written. Handle complex numbers. Prevent harmful promotions. Some of the introduced extended precision arithmetic code will also be used in `base/div.jl` to tackle issue JuliaLang#49450 with PR JuliaLang#49561. TODO: evaluate the accuracy improvements and write tests after JuliaLang#49616 gets merged Results: The example in JuliaLang#33677 still gives the same result. I wasn't able to evaluate JuliaLang#23497 because of how much Julia changed in the meantime. TODO: check whether there are improvements for JuliaLang#26553 Updates JuliaLang#49589
Update the arithmetic code to use algorithms published since twiceprecision.jl got written. Handle complex numbers. Prevent harmful promotions. Some of the introduced extended precision arithmetic code will also be used in `base/div.jl` to tackle issue JuliaLang#49450 with PR JuliaLang#49561. TODO: evaluate the accuracy improvements and write tests after JuliaLang#49616 gets merged Results: The example in JuliaLang#33677 still gives the same result. I wasn't able to evaluate JuliaLang#23497 because of how much Julia changed in the meantime. TODO: check whether there are improvements for JuliaLang#26553 Updates JuliaLang#49589
Disables some constructors to help in catching errors earlier. Disables converting constructors like, for example, `TwicePrecision{Float64}(::BigFloat, ::BigFloat)`. This constructor was actually used in some cases before commit "Base: TwicePrecision: improve constructors (JuliaLang#49616)" (ce2c3ae), so disabling the constructor would have brought the possible accuracy losses and construction of unnormalized double-word numbers to attention sooner. Also effectively *blacklists* some types, to prevent them from being the type of a word in a `TwicePrecision` type. For example, `TwicePrecision{<:Integer}` objects are now disallowed, which is good as such a type would be nonsensical. Blacklisting is required, as opposed to whitelisting, because of the need to support various number-like types that are not `<:Number`. In the same way it is now forbidden to construct `TwicePrecision{T}` objects where `T` is not a concrete type. This should prevent the construction of `TwicePrecision` objects with the high and low words of differing types. Updates JuliaLang#49589
Disables some constructors to help in catching errors earlier. Disables converting constructors like, for example, `TwicePrecision{Float64}(::BigFloat, ::BigFloat)`. This constructor was actually used in some cases before commit "Base: TwicePrecision: improve constructors (JuliaLang#49616)" (ce2c3ae), so disabling the constructor would have brought the possible accuracy losses and construction of unnormalized double-word numbers to attention sooner. Also effectively *blacklists* some types, to prevent them from being the type of a word in a `TwicePrecision` type. For example, `TwicePrecision{<:Integer}` objects are now disallowed, which is good as such a type would be nonsensical. Blacklisting is required, as opposed to whitelisting via type constraints, because of the need to support various floating-point-like types that are not `<:AbstractFloat`, and, more generally, various number-like types that are not `<:Number` (such as types representing physical units). In the same way it is now forbidden to construct `TwicePrecision{T}` objects where `T` is not a concrete type. This should prevent the construction of `TwicePrecision` objects with the high and low words of differing types. Updates JuliaLang#49589
EDIT: when someone gets around to reviewing this, it'd probably be best to skip all the comments, seeing as none are relevant any more after I reduced the PR, leaving the tricky concerns for future PRs
Correctness is improved for constructing
TwicePrecision{T}
fromTwicePrecision{S}
. Previously a call likeTwicePrecision{Float64}(::TwicePrecision{Float32})
would leavehi
andlo
unnormalized; while a call likeTwicePrecision{Float32}(::TwicePrecision{Float64})
would additionally introduce unnecessary truncation.Accuracy is improved for constructing
TwicePrecision
from types likeBigFloat
orRational
. Previously a call likeTwicePrecision{Float64}(::BigFloat)
would unconditionally leavelo
as zero.Example code that tests the improvements (all Boolean values should evaluate to
true
):Updates #49589