Skip to content

Commit a1c1711

Browse files
committed
Use Int256 to avoid BigInt in FD operations.
We do not here explicitly introduce support for FD{BitIntegers.Int256}, though that should work out of the box both before and after this PR. Rather, this PR _uses_ a (U)Int256 under the hood to prevent allocations from Int128 widening to BigInt in FD operations.
1 parent ddee978 commit a1c1711

File tree

2 files changed

+26
-7
lines changed

2 files changed

+26
-7
lines changed

Project.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ authors = ["Fengyang Wang <[email protected]>", "Curtis Vogt <curtis.vog
44
version = "0.5.2"
55

66
[deps]
7+
BitIntegers = "c3b6d118-76ef-56ca-8cc7-ebb389d030a1"
78
Parsers = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0"
89

910
[compat]

src/FixedPointDecimals.jl

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ export checked_abs, checked_add, checked_cld, checked_div, checked_fld,
3535
checked_mod, checked_mul, checked_neg, checked_rem, checked_sub
3636

3737
using Base: decompose, BitInteger
38+
39+
import BitIntegers # For 128-bit _widemul / _widen
3840
import Parsers
3941

4042
# floats that support fma and are roughly IEEE-like
@@ -118,6 +120,22 @@ function __init__()
118120
return
119121
end
120122

123+
# Custom widemul implementation to avoid the cost of widening to BigInt.
124+
# FD{Int128} operations should widen to 256 bits internally, rather than to a BigInt.
125+
const BitInteger128 = Union{Int128, UInt128}
126+
_widemul(x, y) = widemul(x, y)
127+
_widemul(x::BitInteger128, y) = _widemul(promote(x, y)...)
128+
_widemul(x, y::BitInteger128) = _widemul(promote(x, y)...)
129+
_widemul(x::Int128, y::Int128) = BitIntegers.Int256(x) * BitIntegers.Int256(y)
130+
_widemul(x::UInt128, y::UInt128) = BitIntegers.UInt256(x) * BitIntegers.UInt256(y)
131+
132+
# Custom widen implementation to avoid the cost of widening to BigInt.
133+
# FD{Int128} operations should widen to 256 bits internally, rather than to a BigInt.
134+
_widen(::Type{Int128}) = BitIntegers.Int256
135+
_widen(::Type{UInt128}) = BitIntegers.UInt256
136+
_widen(t) = widen(t)
137+
138+
121139
(::Type{T})(x::Real) where {T <: FD} = convert(T, x)
122140

123141
floattype(::Type{<:FD{T}}) where {T<:Union{Int8, UInt8, Int16, UInt16}} = Float32
@@ -188,7 +206,7 @@ _round_to_nearest(q, r, d, m=RoundNearest) = _round_to_nearest(promote(q, r, d).
188206
# correctness test suite.
189207
function Base.:*(x::FD{T, f}, y::FD{T, f}) where {T, f}
190208
powt = coefficient(FD{T, f})
191-
quotient, remainder = fldmodinline(widemul(x.i, y.i), powt)
209+
quotient, remainder = fldmodinline(_widemul(x.i, y.i), powt)
192210
reinterpret(FD{T, f}, _round_to_nearest(quotient, remainder, powt))
193211
end
194212

@@ -416,7 +434,7 @@ function Base.checked_sub(x::T, y::T) where {T<:FD}
416434
end
417435
function Base.checked_mul(x::FD{T,f}, y::FD{T,f}) where {T<:Integer,f}
418436
powt = coefficient(FD{T, f})
419-
quotient, remainder = fldmodinline(widemul(x.i, y.i), powt)
437+
quotient, remainder = fldmodinline(_widemul(x.i, y.i), powt)
420438
v = _round_to_nearest(quotient, remainder, powt)
421439
typemin(T) <= v <= typemax(T) || Base.Checked.throw_overflowerr_binaryop(:*, x, y)
422440
return reinterpret(FD{T, f}, T(v))
@@ -474,7 +492,7 @@ checked_rdiv(x::FD, y::FD) = checked_rdiv(promote(x, y)...)
474492

475493
function checked_rdiv(x::FD{T,f}, y::FD{T,f}) where {T<:Integer,f}
476494
powt = coefficient(FD{T, f})
477-
quotient, remainder = fldmod(widemul(x.i, powt), y.i)
495+
quotient, remainder = fldmod(_widemul(x.i, powt), y.i)
478496
v = _round_to_nearest(quotient, remainder, y.i)
479497
typemin(T) <= v <= typemax(T) || Base.Checked.throw_overflowerr_binaryop(:/, x, y)
480498
return reinterpret(FD{T, f}, v)
@@ -484,8 +502,8 @@ end
484502
# FixedDecimal.
485503
function checked_rdiv(x::Integer, y::FD{T, f}) where {T<:Integer, f}
486504
powt = coefficient(FD{T, f})
487-
powtsq = widemul(powt, powt)
488-
quotient, remainder = fldmod(widemul(x, powtsq), y.i)
505+
powtsq = _widemul(powt, powt)
506+
quotient, remainder = fldmod(_widemul(x, powtsq), y.i)
489507
v = _round_to_nearest(quotient, remainder, y.i)
490508
typemin(T) <= v <= typemax(T) || Base.Checked.throw_overflowerr_binaryop(:/, x, y)
491509
reinterpret(FD{T, f}, v)
@@ -722,7 +740,7 @@ NOTE: This function is expensive, since it contains a while-loop, but it is actu
722740
This function does not have or depend on any side-effects.
723741
"""
724742
function max_exp10(::Type{T}) where {T <: Integer}
725-
W = widen(T)
743+
W = _widen(T)
726744
type_max = W(typemax(T))
727745

728746
powt = one(W)
@@ -759,4 +777,4 @@ value(fd::FD) = fd.i
759777
# for generic hashing
760778
Base.decompose(fd::FD) = decompose(Rational(fd))
761779

762-
end
780+
end # module

0 commit comments

Comments
 (0)