Skip to content

Commit 9627c91

Browse files
authored
Better support for dividing by integers (#6)
* Support divide by integer * Fix rounding * Avoid using BigInt * Minor refactor
1 parent ad06481 commit 9627c91

File tree

2 files changed

+59
-0
lines changed

2 files changed

+59
-0
lines changed

src/FixedPointDecimals.jl

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ import Base: reinterpret, zero, one, abs, sign, ==, <, <=, +, -, /, *, div,
3434
typemin, typemax, realmin, realmax, print, show, string, convert,
3535
promote_rule, min, max, trunc, round, floor, ceil, eps, float, widemul
3636

37+
import Base.Checked: checked_mul
38+
3739
const IEEEFloat = Union{Float16, Float32, Float64}
3840

3941
for fn in [:trunc, :floor, :ceil]
@@ -143,6 +145,21 @@ function /{T, f}(x::FD{T, f}, y::FD{T, f})
143145
reinterpret(FD{T, f}, quotient * powt + round(T, remainder / y.i * powt))
144146
end
145147

148+
# these functions are needed to avoid InexactError when converting from the integer type
149+
function /{T, f}(x::Integer, y::FD{T, f})
150+
powt = T(10)^f
151+
xi, yi = checked_mul(x, powt), y.i
152+
quotient, remainder = divrem(xi, yi)
153+
reinterpret(FD{T, f}, quotient * powt + round(T, remainder / yi * powt))
154+
end
155+
156+
function /{T, f}(x::FD{T, f}, y::Integer)
157+
powt = T(10)^f
158+
xi, yi = x.i, checked_mul(y, powt)
159+
quotient, remainder = divrem(xi, yi)
160+
reinterpret(FD{T, f}, quotient * powt + round(T, remainder / yi * powt))
161+
end
162+
146163
# integerification
147164
trunc{T, f}(x::FD{T, f}) = FD{T, f}(div(x.i, T(10)^f))
148165
floor{T, f}(x::FD{T, f}) = FD{T, f}(fld(x.i, T(10)^f))

test/runtests.jl

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,12 @@ end
186186
end
187187
@test prod(keyvalues[T]) == 0
188188
end
189+
190+
@testset "without promotion" begin
191+
@test_throws InexactError FD{Int8,1}(20)
192+
@test 20 * FD{Int8,1}(0.1) == FD{Int8,1}(2.0)
193+
@test FD{Int8,1}(0.1) * 20 == FD{Int8,1}(2.0)
194+
end
189195
end
190196

191197
@testset "division" begin
@@ -238,6 +244,42 @@ end
238244
@testset "divide $x by 0" for x in keyvalues[FD2]
239245
@test_throws DivideError x/FD2(0)
240246
end
247+
248+
@testset "rounding" begin
249+
# RoundNearest: 1.27 / 2 == 0.635 rounds up to 0.64
250+
@test FD2(1.27) / FD2(2) == FD2(0.64)
251+
@test FD2(-1.27) / FD2(2) == FD2(-0.64)
252+
@test FD2(1.27) / 2 == FD2(0.64)
253+
@test FD2(-1.27) / 2 == FD2(-0.64)
254+
@test 127 / FD2(200) == FD2(0.64)
255+
@test -127 / FD2(200) == FD2(-0.64)
256+
257+
# RoundNearest: 1.29 / 2 == 0.645 rounds down to 0.64
258+
@test FD2(1.29) / FD2(2) == FD2(0.64)
259+
@test FD2(-1.29) / FD2(2) == FD2(-0.64)
260+
@test FD2(1.29) / 2 == FD2(0.64)
261+
@test FD2(-1.29) / 2 == FD2(-0.64)
262+
@test 129 / FD2(200) == FD2(0.64)
263+
@test -129 / FD2(200) == FD2(-0.64)
264+
265+
# Use of Float or BigFloat internally can change the calculated result
266+
@test round(Int, 109 / 200 * 100) == 55
267+
@test round(Int, BigInt(109) / 200 * 100) == 54 # Correct
268+
269+
x = FD{Int128,2}(1.09)
270+
@test x / Int128(2) != x / BigInt(2)
271+
@test x / FD{Int128,2}(2) == x / Int128(2)
272+
273+
y = FD{Int128,2}(200)
274+
@test Int128(109) / y != BigInt(109) / y
275+
@test FD{Int128,2}(109) / y == Int128(109) / y
276+
end
277+
278+
@testset "without promotion" begin
279+
@test_throws InexactError FD{Int8,1}(20)
280+
@test 20 / FD{Int8,1}(2) == FD{Int8,1}(10.0)
281+
@test FD{Int8,1}(2) / 20 == FD{Int8,1}(0.1)
282+
end
241283
end
242284

243285
@testset "abs, sign" begin

0 commit comments

Comments
 (0)