Skip to content

Commit c81bb83

Browse files
committed
Let all the FixedDecimals operations overflow, matching Int overflow
1 parent b923f29 commit c81bb83

File tree

2 files changed

+47
-7
lines changed

2 files changed

+47
-7
lines changed

src/FixedPointDecimals.jl

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -187,13 +187,13 @@ end
187187

188188
# these functions are needed to avoid InexactError when converting from the
189189
# integer type
190-
Base.:*(x::Integer, y::FD{T, f}) where {T, f} = reinterpret(FD{T, f}, T(x * y.i))
191-
Base.:*(x::FD{T, f}, y::Integer) where {T, f} = reinterpret(FD{T, f}, T(x.i * y))
190+
Base.:*(x::Integer, y::FD{T, f}) where {T, f} = reinterpret(FD{T, f}, *(promote(x, y.i)...))
191+
Base.:*(x::FD{T, f}, y::Integer) where {T, f} = reinterpret(FD{T, f}, *(promote(x.i, y)...))
192192

193193
function Base.:/(x::FD{T, f}, y::FD{T, f}) where {T, f}
194194
powt = coefficient(FD{T, f})
195195
quotient, remainder = fldmod(widemul(x.i, powt), y.i)
196-
reinterpret(FD{T, f}, T(_round_to_nearest(quotient, remainder, y.i)))
196+
reinterpret(FD{T, f}, _round_to_nearest(quotient, remainder, y.i))
197197
end
198198

199199
# These functions allow us to perform division with integers outside of the range of the
@@ -202,12 +202,12 @@ function Base.:/(x::Integer, y::FD{T, f}) where {T, f}
202202
powt = coefficient(FD{T, f})
203203
powtsq = widemul(powt, powt)
204204
quotient, remainder = fldmod(widemul(x, powtsq), y.i)
205-
reinterpret(FD{T, f}, T(_round_to_nearest(quotient, remainder, y.i)))
205+
reinterpret(FD{T, f}, _round_to_nearest(quotient, remainder, y.i))
206206
end
207207

208208
function Base.:/(x::FD{T, f}, y::Integer) where {T, f}
209209
quotient, remainder = fldmod(x.i, y)
210-
reinterpret(FD{T, f}, T(_round_to_nearest(quotient, remainder, y)))
210+
reinterpret(FD{T, f}, _round_to_nearest(quotient, remainder, y))
211211
end
212212

213213
# integerification
@@ -362,12 +362,22 @@ end
362362
for divfn in [:div, :fld, :fld1, :cld]
363363
# div(x.i, y.i) eliminates the scaling coefficient, so we call the FD constructor.
364364
# We don't need any widening logic, since we won't be multiplying by the coefficient.
365-
@eval Base.$divfn(x::T, y::T) where {T <: FD} = T($divfn(x.i, y.i))
365+
#@eval Base.$divfn(x::T, y::T) where {T <: FD} = T($divfn(x.i, y.i))
366+
# @eval Base.$divfn(x::T, y::T) where {T <: FD} = $divfn(promote(x.i, y.i)...)
367+
# TODO(PR): I'm not sure about this one...
368+
# What should it *mean* for `typemax(FD) ÷ FD(0.5)` to overflow?
369+
@eval function Base.$divfn(x::T, y::T) where {T <: FD}
370+
C = coefficient(T)
371+
return reinterpret(T, C * $divfn(promote(x.i, y.i)...))
372+
end
366373
end
367374
if VERSION >= v"1.4.0-"
368375
# div(x.i, y.i) eliminates the scaling coefficient, so we call the FD constructor.
369376
# We don't need any widening logic, since we won't be multiplying by the coefficient.
370-
Base.div(x::T, y::T, r::RoundingMode) where {T <: FD} = T(div(x.i, y.i, r))
377+
@eval function Base.div(x::T, y::T, r::RoundingMode) where {T <: FD}
378+
C = coefficient(T)
379+
return reinterpret(T, C * div(x.i, y.i, r))
380+
end
371381
end
372382

373383
Base.convert(::Type{AbstractFloat}, x::FD) = convert(floattype(typeof(x)), x)

test/FixedDecimal.jl

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,36 @@ end
712712
end
713713
end
714714

715+
@testset "overflow" begin
716+
@testset "addition" begin
717+
@test typemax(FD2) + eps(FD2) == typemin(FD2)
718+
@test typemin(FD2) + (-eps(FD2)) == typemax(FD2)
719+
end
720+
721+
@testset "subtraction" begin
722+
@test typemin(FD2) - eps(FD2) == typemax(FD2)
723+
@test typemax(FD2) - (-eps(FD2)) == typemin(FD2)
724+
end
725+
726+
@testset "multiplication" begin
727+
@test typemax(FD2) * 2 == FD2(-0.02)
728+
@test typemin(FD2) * 2 == FD2(0)
729+
end
730+
731+
@testset "division" begin
732+
@test typemax(FD2) / FD2(0.5) == FD2(-0.02)
733+
@test typemin(FD2) / FD2(0.5) == FD2(0)
734+
end
735+
736+
@testset "truncating division" begin
737+
# TODO(PR): Is this the expected value?
738+
@test typemax(FD2) ÷ FD2(0.5) == FD2(-0.16)
739+
@test typemin(FD2) ÷ FD2(0.5) == FD2(0.16)
740+
@test typemax(FD2) ÷ eps(FD2) == FD2(-1)
741+
@test typemin(FD2) ÷ eps(FD2) == FD2(0)
742+
end
743+
end
744+
715745
@testset "isinteger" begin
716746
# Note: Test cannot be used unless we can construct `FD{Int8,6}`
717747
# @testset "overflow" begin

0 commit comments

Comments
 (0)