Skip to content

Commit d42dd35

Browse files
committed
Framework for fixing overflow issues
When the number of decimal places `f` produces a coefficient `10^f` which is not representable in the container type `T` we can run into overflow issues. Additionally, if the FixedDecimal value is also large we can also run into overflow when working with a representable coefficient. The new function `coefficient` provides us with a representable power of 10 which uses a type which will never overflow when multiplied by the value.
1 parent f468058 commit d42dd35

File tree

2 files changed

+43
-10
lines changed

2 files changed

+43
-10
lines changed

src/FixedPointDecimals.jl

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@ export FixedDecimal, RoundThrows
2929

3030
using Compat
3131

32-
import Base: reinterpret, zero, one, abs, sign, ==, <, <=, +, -, /, *, div,
33-
rem, divrem, fld, mod, fldmod, fld1, mod1, fldmod1, isinteger,
34-
typemin, typemax, realmin, realmax, print, show, string, convert, parse,
35-
promote_rule, min, max, trunc, round, floor, ceil, eps, float, widemul
32+
import Base: reinterpret, zero, one, abs, sign, ==, <, <=, +, -, /, *, div, rem, divrem,
33+
fld, mod, fldmod, fld1, mod1, fldmod1, isinteger, typemin, typemax,
34+
realmin, realmax, print, show, string, convert, parse, promote_rule, min, max,
35+
trunc, round, floor, ceil, eps, float, widemul, exp10
3636

3737
import Base.Checked: checked_mul
3838

@@ -283,7 +283,7 @@ function print{T, f}(io::IO, x::FD{T, f})
283283

284284
# note: a is negative if x.i == typemin(x.i)
285285
s, a = sign(x.i), abs(x.i)
286-
integer, fractional = divrem(a, exp10(T, f))
286+
integer, fractional = divrem(a, coefficient(x))
287287
integer = abs(integer) # ...but since f > 0, this is positive
288288
fractional = abs(fractional)
289289

@@ -382,15 +382,33 @@ function parse_round{T}(::Type{T}, fractional::AbstractString, ::RoundingMode{:N
382382
return T(0)
383383
end
384384

385-
function exp10_max{T}(::Type{T})
385+
386+
"""
387+
max_exp10(T)
388+
389+
The highest value of `x` which does not result in an overflow when evaluating `T(10)^x`.
390+
"""
391+
function max_exp10{T <: Integer}(::Type{T})
386392
length(digits(typemax(T))) - 1
387393
end
388394

389-
function exp10(T::Type, y::Integer)
390-
while T != BigInt && exp10_max(T) < y
391-
T = widen(T)
395+
"""
396+
exp10(::Type{FD{T, f}})
397+
398+
Compute `10^f` as an Integer without overflow. The resulting type will be an integer of type
399+
T or wider.
400+
"""
401+
@generated function exp10{T <: Integer, f}(::Type{FD{T, f}})
402+
P = T
403+
while P != BigInt && f > max_exp10(P)
404+
P = widen(P)
405+
end
406+
quote
407+
$(P(10)^f)
392408
end
393-
T(10)^y
394409
end
395410

411+
coefficient{T, f}(fd::FD{T, f}) = widen(exp10(FD{T, f}))
412+
value(fd::FD) = fd.i
413+
396414
end

test/runtests.jl

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ using FixedPointDecimals
22
import FixedPointDecimals: FD
33
using Base.Test
44
using Compat
5+
import Base.Checked: checked_mul
56

67
@testset "FixedPointDecimals" begin
78

@@ -64,6 +65,20 @@ function parse_int{T, f}(::Type{FD{T, f}}, val::AbstractString; ceil::Bool=false
6465
reinterpret(FD{T, f}, parse(T, val[1:(f + 1)]) + T(ceil))
6566
end
6667

68+
# ensure that the coefficient multiplied by the highest and lowest representable values of
69+
# the container type do not result in overflow.
70+
@testset "coefficient" begin
71+
container_types = [Int8, Int16, Int32, Int64, Int128]
72+
for (i, T) in enumerate(container_types)
73+
for j in i:length(container_types)
74+
f = FixedPointDecimals.max_exp10(container_types[j])
75+
powt = FixedPointDecimals.coefficient(FD{T, f}(0))
76+
@test checked_mul(powt, typemax(T)) == powt * typemax(T)
77+
@test checked_mul(powt, typemin(T)) == powt * typemin(T)
78+
end
79+
end
80+
end
81+
6782
@testset "conversion" begin
6883
@testset for x in keyvalues[FD2]
6984
@testset for T in [Rational{Int128}, WFD2, WFD4]

0 commit comments

Comments
 (0)