-
-
Notifications
You must be signed in to change notification settings - Fork 5.6k
Description
Algorithms for arithmetic (+
, -
, *
, /
)
The algorithms for real (as opposed to complex) double-word addition, multiplication and division were seemingly (there are no relevant explanatory comments or references in the code) all created ad-hoc and need to be updated to the standard algos, something I'll try to do in #49569.
Regarding the algorithms for complex numbers, they seem to be missing entirely, causing Base.TwicePrecision{<:Complex}
to fall back to the algorithms for real numbers. This is OK for addition because Complex
is a Cartesian (orthogonal) representation of complex numbers, but multiplication and division could be quite broken. This could be fixed by adding methods for Base.TwicePrecision{<:Complex}
to base/complex.jl
. A paper exists for double-word complex multiplication, and I guess I could work something out for division.
Design of the Base.TwicePrecision
type
The Base.TwicePrecision
type has an awkward and unsafe design. It accepts elements of any type, not just floating-point-based types. I propose this design instead as a replacement:
In base/twiceprecision.jl
:
abstract type AbstractMultiWordFloatingPoint{n,T<:Number} end
# some methods for `AbstractMultiWordFloatingPoint` ...
struct MultiWordFloatingPoint{n,T<:AbstractFloat} <:
AbstractMultiWordFloatingPoint{n,T}
words::NTuple{n,T}
end
# some methods for `MultiWordFloatingPoint` ...
const DoubleWordFloatingPoint =
MultiWordFloatingPoint{2,T} where {T<:AbstractFloat}
# some methods for `DoubleWordFloatingPoint`, including arithmetic ...
In base/complex.jl
:
struct MultiWordFloatingPointComplex{n,T<:Complex{<:AbstractFloat}} <:
AbstractMultiWordFloatingPoint{n,T}
words::NTuple{n,T}
end
# some methods for `MultiWordFloatingPointComplex` ...
const DoubleWordFloatingPointComplex =
MultiWordFloatingPointComplex{2,T} where {T<:AbstractFloat}
# some methods for `DoubleWordFloatingPointComplex`, including arithmetic ...
Note that MultiWordFloatingPointComplex
only makes sense because Complex
is an orthogonal coordinate representation of the complex numbers. A polar representation, for example, would be nonsensical in this context because a multi-word representation represents an unevaluated sum.
The n
type parameter would be restricted to two in practice for the time being, however I think that having it as a parameter right away gives a more elegant design.
Is supporting complex numbers even necessary?
The point of Base.TwicePrecision
is supporting ranges, and while ranges with complex elements do exist, I think the step is always real? This seems like it would mean that double-word algorithms for complex numbers are an unnecessary evil.
EDIT: the answer seems to be yes, supporting complex numbers is necessary:
julia> (1.0:5.0) .* im
0.0 + 1.0im:0.0 + 1.0im:0.0 + 5.0im
julia> (1.0:5.0) .* im .* im
-1.0 + 0.0im:-1.0 + 0.0im:-5.0 + 0.0im
splitprec
This is more of a question than an issue, but I find this dubious so I thought it'd be good to raise it here with the other stuff.
Why does TwicePrecision{<:AbstractFloat}(::Integer)
use that splitprec
function:
https://github.com/JuliaLang/julia/blob/master/base/twiceprecision.jl#L213-L214
The fallback conversion method TwicePrecision{<:Any}(::Any)
is sufficient for converting integers to double-word floating-point:
https://github.com/JuliaLang/julia/blob/master/base/twiceprecision.jl#L203-L207
So it seems that the intention is to give up accuracy on purpose while converting integers to TwicePrecision
? It's likely that I'm missing something, but I find this quite strange and I can't imagine why would one desire such behavior. Also, it seems that something similar is happening when the truncbits
function is used.