Skip to content

Commit 2aec095

Browse files
committed
Fix division at the input limits
1 parent cc62fc0 commit 2aec095

File tree

2 files changed

+54
-17
lines changed

2 files changed

+54
-17
lines changed

src/FixedPointDecimals.jl

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -140,24 +140,24 @@ end
140140

141141
# TODO. this is probably wrong sometimes.
142142
function /{T, f}(x::FD{T, f}, y::FD{T, f})
143-
powt = T(10)^f
143+
powt = coefficient(FD{T,f})
144144
quotient, remainder = divrem(x.i, y.i)
145-
reinterpret(FD{T, f}, quotient * powt + round(T, remainder / y.i * powt))
145+
reinterpret(FD{T, f}, T(widemul(quotient, powt) + round(T, remainder // y.i * powt)))
146146
end
147147

148148
# these functions are needed to avoid InexactError when converting from the integer type
149149
function /{T, f}(x::Integer, y::FD{T, f})
150-
powt = T(10)^f
150+
powt = coefficient(FD{T,f})
151151
xi, yi = checked_mul(x, powt), y.i
152152
quotient, remainder = divrem(xi, yi)
153-
reinterpret(FD{T, f}, quotient * powt + round(T, remainder / yi * powt))
153+
reinterpret(FD{T, f}, T(widemul(quotient, powt) + round(T, remainder // yi * powt)))
154154
end
155155

156156
function /{T, f}(x::FD{T, f}, y::Integer)
157-
powt = T(10)^f
157+
powt = coefficient(FD{T,f})
158158
xi, yi = x.i, checked_mul(y, powt)
159159
quotient, remainder = divrem(xi, yi)
160-
reinterpret(FD{T, f}, quotient * powt + round(T, remainder / yi * powt))
160+
reinterpret(FD{T, f}, T(widemul(quotient, powt) + round(T, remainder // yi * powt)))
161161
end
162162

163163
# integerification

test/runtests.jl

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,13 @@ end
256256
@testset "division by 1" begin
257257
@testset for x in keyvalues[FD2]
258258
@test x / one(x) == x
259-
@test x / -one(x) == -x
259+
260+
# signed integers using two's complement have one additional negative value
261+
if x < 0 && x == typemin(x)
262+
@test_throws InexactError x / -one(x)
263+
else
264+
@test x / -one(x) == -x
265+
end
260266
end
261267
end
262268

@@ -320,24 +326,55 @@ end
320326
@test 129 / FD2(200) == FD2(0.64)
321327
@test -129 / FD2(200) == FD2(-0.64)
322328

323-
# Use of Float or BigFloat internally can change the calculated result
324-
@test round(Int, 109 / 200 * 100) == 55
325-
@test round(Int, BigInt(109) / 200 * 100) == 54 # Correct
326-
327-
x = FD{Int128,2}(1.09)
328-
@test x / Int128(2) != x / BigInt(2)
329-
@test x / FD{Int128,2}(2) == x / Int128(2)
329+
# Using a floating-point number internally can slightly change the result
330+
@test round(Int, 109 / 200 * 100) == 55 # Wrong
331+
@test round(Int, BigInt(109) / 200 * 100) == 54
332+
@test round(Int, 109 // 200 * 100) == 54
330333

331-
y = FD{Int128,2}(200)
332-
@test Int128(109) / y != BigInt(109) / y
333-
@test FD{Int128,2}(109) / y == Int128(109) / y
334+
x = FD2(1.09)
335+
y = FD2(200)
336+
for T in [FD2, Int8, Int128, BigInt]
337+
@test x / T(2) == FD2(0.54)
338+
@test T(109) / y == FD2(0.54)
339+
end
334340
end
335341

336342
@testset "without promotion" begin
337343
@test_throws InexactError FD{Int8,1}(20)
338344
@test 20 / FD{Int8,1}(2) == FD{Int8,1}(10.0)
339345
@test FD{Int8,1}(2) / 20 == FD{Int8,1}(0.1)
340346
end
347+
348+
@testset "limits" begin
349+
@test_throws InexactError 1 / FD{Int8,2}(0.4)
350+
@test_throws InexactError FD{Int8,2}(1) / FD{Int8,2}(0.4)
351+
352+
for T in CONTAINER_TYPES
353+
x = FixedPointDecimals.max_exp10(T)
354+
f = x + 1
355+
356+
scalar = reinterpret(FD{T,f}, T(10)^x) # 0.1
357+
358+
# Since multiply will round the result we'll make sure our value always
359+
# rounds down.
360+
max_int = typemax(T) - (typemax(T) % 10)
361+
min_int = typemin(T) - (typemin(T) % 10)
362+
max_fd = reinterpret(FD{T,f}, max_int)
363+
min_fd = reinterpret(FD{T,f}, min_int)
364+
365+
@eval begin
366+
@test ($max_fd * $scalar) / $scalar == $max_fd
367+
@test ($min_fd * $scalar) / $scalar == $min_fd
368+
@test $max_fd / 2 == reinterpret(FD{$T,$f}, div($max_int, 2))
369+
@test $min_fd / 2 == reinterpret(FD{$T,$f}, div($min_int, 2))
370+
371+
# Since the precision of `f` doesn't allow us to make a FixedDecimal >= 1
372+
# there is no way testing this function without raising an exception.
373+
$max_fd != 0 && @test_throws InexactError 2 / $max_fd
374+
$min_fd != 0 && @test_throws InexactError 2 / $min_fd
375+
end
376+
end
377+
end
341378
end
342379

343380
@testset "abs, sign" begin

0 commit comments

Comments
 (0)