Skip to content

Commit cbafd58

Browse files
committed
Implement round methods for all RoundingModes
1 parent 1328b9a commit cbafd58

File tree

1 file changed

+32
-13
lines changed

1 file changed

+32
-13
lines changed

src/FixedPointDecimals.jl

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -125,24 +125,36 @@ end
125125
Base.widemul(x::Integer, y::FD) = widemul(y, x)
126126

127127
"""
128-
_round_to_even(quotient, remainder, divisor)
129-
130-
Round `quotient + remainder / divisor` to the nearest even integer, given that
131-
`0 ≤ remainder < divisor` or `0 ≥ remainder > divisor`. (This assumption is
132-
satisfied by the return value of `fldmod` in all cases, and the return value of
133-
`divrem` in cases where `divisor` is known to be positive.)
128+
_round_to_even(quotient, remainder, divisor, ::RoundingMode{M})
129+
130+
Round `quotient + remainder / divisor` to the nearest even integer,
131+
given that `0 ≤ remainder < divisor` or `0 ≥ remainder >
132+
divisor`. (This assumption is satisfied by the return value of
133+
`fldmod` in all cases, and the return value of `divrem` in cases where
134+
`divisor` is known to be positive.) The tie is decided depending on
135+
the `RoundingMode`.
134136
"""
135-
function _round_to_even(quotient::T, remainder::T, divisor::T) where {T <: Integer}
137+
function _round_to_even(quotient::T, remainder::T, divisor::T, ::RoundingMode{M}=RoundNearest) where {T <: Integer, M}
136138
halfdivisor = divisor >> 1
137139
if iseven(divisor) && remainder == halfdivisor
138-
ifelse(iseven(quotient), quotient, quotient + one(quotient))
140+
# `:NearestTiesAway` will tie away from zero, e.g. -8.5 ->
141+
# -9. `:NearestTiesUp` will always ties towards positive
142+
# infinity. `:Nearest` will tie towards the nearest even
143+
# integer.
144+
if M == :NearestTiesAway && quotient < zero(quotient)
145+
quotient
146+
elseif M == :Nearest && iseven(quotient)
147+
quotient
148+
else
149+
quotient + one(quotient)
150+
end
139151
elseif abs(remainder) > abs(halfdivisor)
140152
quotient + one(quotient)
141153
else
142154
quotient
143155
end
144156
end
145-
_round_to_even(q, r, d) = _round_to_even(promote(q, r, d)...)
157+
_round_to_even(q, r, d, m=RoundNearest) = _round_to_even(promote(q, r, d)..., m)
146158

147159
# In many of our calls to fldmod, `y` is a constant (the coefficient, 10^f). However, since
148160
# `fldmod` is sometimes not being inlined, that constant information is not available to the
@@ -188,11 +200,18 @@ end
188200
Base.trunc(x::FD{T, f}) where {T, f} = FD{T, f}(div(x.i, coefficient(FD{T, f})))
189201
Base.floor(x::FD{T, f}) where {T, f} = FD{T, f}(fld(x.i, coefficient(FD{T, f})))
190202

203+
Base.round(fd::FD, ::RoundingMode{:Up}) = ceil(fd)
204+
Base.round(fd::FD, ::RoundingMode{:Down}) = floor(fd)
205+
Base.round(fd::FD, ::RoundingMode{:ToZero}) = trunc(fd)
206+
191207
# TODO: round with number of digits; should be easy
192-
function Base.round(x::FD{T, f}, ::RoundingMode{:Nearest}=RoundNearest) where {T, f}
208+
function Base.round(x::FD{T, f},
209+
m::Union{RoundingMode{:Nearest},
210+
RoundingMode{:NearestTiesUp},
211+
RoundingMode{:NearestTiesAway}}=RoundNearest) where {T, f}
193212
powt = coefficient(FD{T, f})
194213
quotient, remainder = fldmodinline(x.i, powt)
195-
FD{T, f}(_round_to_even(quotient, remainder, powt))
214+
FD{T, f}(_round_to_even(quotient, remainder, powt, m))
196215
end
197216
function Base.ceil(x::FD{T, f}) where {T, f}
198217
powt = coefficient(FD{T, f})
@@ -247,8 +266,8 @@ for fn in [:trunc, :floor, :ceil]
247266
reinterpret(FD{T, f}, val)
248267
end
249268
end
250-
function Base.round(::Type{TI}, x::FD, ::RoundingMode{:Nearest}=RoundNearest) where {TI <: Integer}
251-
convert(TI, round(x))::TI
269+
function Base.round(::Type{TI}, x::FD, m::RoundingMode=RoundNearest) where {TI <: Integer}
270+
convert(TI, round(x,m))::TI
252271
end
253272
function Base.round(::Type{FD{T, f}}, x::Real, ::RoundingMode{:Nearest}=RoundNearest) where {T, f}
254273
reinterpret(FD{T, f}, round(T, x * coefficient(FD{T, f})))

0 commit comments

Comments
 (0)