Skip to content

Commit 5814884

Browse files
committed
Base: make TwicePrecision more accurate
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
1 parent 1082f7b commit 5814884

File tree

3 files changed

+398
-70
lines changed

3 files changed

+398
-70
lines changed

base/complex.jl

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1125,3 +1125,190 @@ function complex(A::AbstractArray{T}) where T
11251125
end
11261126
convert(AbstractArray{typeof(complex(zero(T)))}, A)
11271127
end
1128+
1129+
const ComplexFloat = Complex{<:AbstractFloat}
1130+
1131+
## Multi-word floating-point for `Complex`
1132+
1133+
complex_twiceprec(real::Tw, imag::Tw) where {Tw<:TwicePrecision{<:AbstractFloat}} =
1134+
TwicePrecision(
1135+
complex(real.hi, imag.hi),
1136+
complex(real.lo, imag.lo),
1137+
)
1138+
1139+
real(c::TwicePrecision{<:ComplexFloat}) =
1140+
TwicePrecision(real(c.hi), real(c.lo))
1141+
1142+
imag(c::TwicePrecision{<:ComplexFloat}) =
1143+
TwicePrecision(imag(c.hi), imag(c.lo))
1144+
1145+
# +
1146+
1147+
function +(x::TwicePrecision{<:AbstractFloat}, y::ComplexFloat)
1148+
y_re = real(y)
1149+
y_im = imag(y)
1150+
sum = x + y_re
1151+
TwicePrecision(complex(sum.hi, y_im), complex(sum.lo))
1152+
end
1153+
1154+
function +(
1155+
x::TwicePrecision{<:ComplexFloat},
1156+
y::Union{AbstractFloat,TwicePrecision{<:AbstractFloat}},
1157+
)
1158+
x_re = real(x)
1159+
x_im = imag(x)
1160+
sum = x_re + y
1161+
complex_twiceprec(sum, x_im)
1162+
end
1163+
1164+
+(x::TwicePrecision{T}, y::T) where {T<:ComplexFloat} =
1165+
TwicePrecision(mw_operate(+, Tuple(x), (y,))...)
1166+
1167+
+(x::TwicePrecision{<:AbstractFloat}, y::TwicePrecision{<:ComplexFloat}) =
1168+
y + x
1169+
1170+
+(x::Tw, y::Tw) where {Tw<:TwicePrecision{<:ComplexFloat}} =
1171+
TwicePrecision(mw_operate(+, Tuple(x), Tuple(y))...)
1172+
1173+
# -
1174+
1175+
-(x::TwicePrecision{<:AbstractFloat}, y::ComplexFloat) =
1176+
x + -y
1177+
1178+
-(x::TwicePrecision{<:ComplexFloat}, y::Union{AbstractFloat,ComplexFloat}) =
1179+
x + -y
1180+
1181+
-(
1182+
x::TwicePrecision{<:ComplexFloat},
1183+
y::TwicePrecision{<:Union{AbstractFloat,ComplexFloat}},
1184+
) =
1185+
x + -y
1186+
1187+
-(x::TwicePrecision{<:ComplexFloat}, y::TwicePrecision{<:AbstractFloat}) =
1188+
x + -y
1189+
1190+
# *
1191+
1192+
function *(x::TwicePrecision{<:AbstractFloat}, y::ComplexFloat)
1193+
y_re = real(y)
1194+
y_im = imag(y)
1195+
res_re = x * y_re
1196+
res_im = x * y_im
1197+
complex_twiceprec(res_re, res_im)
1198+
end
1199+
1200+
function *(
1201+
x::TwicePrecision{<:ComplexFloat},
1202+
y::Union{AbstractFloat,TwicePrecision{<:AbstractFloat}},
1203+
)
1204+
x_re = real(x)
1205+
x_im = imag(x)
1206+
res_re = x_re * y
1207+
res_im = x_im * y
1208+
complex_twiceprec(res_re, res_im)
1209+
end
1210+
1211+
function *(x::Tw, y::Union{T,Tw}) where {T<:ComplexFloat,Tw<:TwicePrecision{T}}
1212+
# TODO: evaluate whether it makes sense to write custom code, with
1213+
# separate methods for `*(x::Tw, y::T)` and `*(x::Tw, y::Tw)`.
1214+
1215+
# TODO: try preventing some overflows and underflows?
1216+
1217+
# TODO: for `*(x::Tw, y::T)`: evaluate whether it makes sense to
1218+
# use Algorithm 3 from the paper "Accurate Complex Multiplication
1219+
# in Floating-Point Arithmetic", https://hal.science/hal-02001080v2
1220+
# The algorithm must first be adapted to return double-word results
1221+
# as explained in the same paper.
1222+
1223+
x_re = real(x)
1224+
x_im = imag(x)
1225+
y_re = real(y)
1226+
y_im = imag(y)
1227+
res_re = x_re*y_re - x_im*y_im
1228+
res_im = x_im*y_re + x_re*y_im
1229+
complex_twiceprec(res_re, res_im)
1230+
end
1231+
1232+
*(x::TwicePrecision{<:AbstractFloat}, y::TwicePrecision{<:ComplexFloat}) =
1233+
y * x
1234+
1235+
# abs2
1236+
1237+
function abs2(x::TwicePrecision{<:ComplexFloat})
1238+
# TODO: evaluate whether it makes sense to write custom code
1239+
1240+
# TODO: try preventing some overflows and underflows?
1241+
1242+
re = real(x)
1243+
im = imag(x)
1244+
re*re + im*im
1245+
end
1246+
1247+
# /
1248+
1249+
function div_complex_twiceprec_impl(
1250+
x::Union{AbstractFloat,TwicePrecision{<:AbstractFloat}},
1251+
y,
1252+
)
1253+
# TODO: use the inverse of abs2 instead for better performance?
1254+
a = abs2(y)
1255+
1256+
y_re = real(y)
1257+
y_im = imag(y)
1258+
res_re = x*y_re/a
1259+
res_im = -x*y_im/a
1260+
complex_twiceprec(res_re, res_im)
1261+
end
1262+
1263+
function div_complex_twiceprec_impl(
1264+
x::Union{ComplexFloat,TwicePrecision{<:ComplexFloat}},
1265+
y,
1266+
)
1267+
# TODO: use the inverse of abs2 instead for better performance?
1268+
a = abs2(y)
1269+
1270+
x_re = real(x)
1271+
x_im = imag(x)
1272+
y_re = real(y)
1273+
y_im = imag(y)
1274+
res_re = (x_re*y_re + x_im*y_im) / a
1275+
res_im = (x_im*y_re - x_re*y_im) / a
1276+
complex_twiceprec(res_re, res_im)
1277+
end
1278+
1279+
/(
1280+
x::TwicePrecision{<:AbstractFloat},
1281+
y::Union{ComplexFloat,TwicePrecision{<:ComplexFloat}},
1282+
) =
1283+
# TODO: evaluate whether it makes sense to write custom code
1284+
# TODO: try preventing some overflows and underflows?
1285+
div_complex_twiceprec_impl(x, y)
1286+
1287+
/(
1288+
x::AbstractFloat,
1289+
y::TwicePrecision{<:ComplexFloat},
1290+
) =
1291+
# TODO: evaluate whether it makes sense to write custom code
1292+
# TODO: try preventing some overflows and underflows?
1293+
div_complex_twiceprec_impl(x, y)
1294+
1295+
function /(
1296+
x::TwicePrecision{<:ComplexFloat},
1297+
y::Union{AbstractFloat,TwicePrecision{<:AbstractFloat}},
1298+
)
1299+
x_re = real(x)
1300+
x_im = imag(x)
1301+
res_re = x_re / y
1302+
res_im = x_im / y
1303+
complex_twiceprec(res_re, res_im)
1304+
end
1305+
1306+
/(x::Tw, y::Union{T,Tw}) where {T<:ComplexFloat,Tw<:TwicePrecision{T}} =
1307+
# TODO: evaluate whether it makes sense to write custom code
1308+
# TODO: try preventing some overflows and underflows?
1309+
div_complex_twiceprec_impl(x, y)
1310+
1311+
/(x::ComplexFloat, y::TwicePrecision{<:ComplexFloat}) =
1312+
# TODO: evaluate whether it makes sense to write custom code
1313+
# TODO: try preventing some overflows and underflows?
1314+
div_complex_twiceprec_impl(x, y)

base/mpfr.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -986,6 +986,11 @@ isfinite(x::BigFloat) = !isinf(x) && !isnan(x)
986986
iszero(x::BigFloat) = x == Clong(0)
987987
isone(x::BigFloat) = x == Clong(1)
988988

989+
# The branchy version should be faster for `BigFloat` because MPFR
990+
# arithmetic is expensive, and the branchless version requires more
991+
# arithmetic.
992+
Base.add12(x::T, y::T) where {T<:BigFloat} = Base.add12_branchful(x, y)
993+
989994
@eval typemax(::Type{BigFloat}) = $(BigFloat(Inf))
990995
@eval typemin(::Type{BigFloat}) = $(BigFloat(-Inf))
991996

0 commit comments

Comments
 (0)