@@ -29,10 +29,10 @@ export FixedDecimal, RoundThrows
2929
3030using 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
3636
3737const IEEEFloat = Union{Float16, Float32, Float64}
3838
@@ -70,17 +70,26 @@ for fn in [:trunc, :floor, :ceil]
7070end
7171
7272"""
73- FixedDecimal{I <: Integer, f::Int}
73+ FixedDecimal{T <: Integer, f::Int}
7474
75- A fixed-point decimal type backed by integral type `I `, with `f` digits after
75+ A fixed-point decimal type backed by integral type `T `, with `f` digits after
7676the decimal point stored.
7777"""
7878immutable FixedDecimal{T <: Integer , f} <: Real
7979 i:: T
8080
81- # internal constructor
82- Base. reinterpret {T, f} (:: Type{FixedDecimal{T, f}} , i:: Integer ) =
83- new {T, f} (i % T)
81+ # inner constructor
82+ function Base. reinterpret {T, f} (:: Type{FixedDecimal{T, f}} , i:: Integer )
83+ n = max_exp10 (T)
84+ if f >= 0 && (n < 0 || f <= n)
85+ new {T, f} (i % T)
86+ else
87+ throw (ArgumentError (
88+ " Requested number of decimal places $f exceeds the max allowed for the " *
89+ " storage type $T : [0, $(max_exp10 (T)) ]"
90+ ))
91+ end
92+ end
8493end
8594
8695const FD = FixedDecimal
@@ -131,8 +140,8 @@ _round_to_even(q, r, d) = _round_to_even(promote(q, r, d)...)
131140# TODO : can we use floating point to speed this up? after we build a
132141# correctness test suite.
133142function * {T, f}(x:: FD{T, f} , y:: FD{T, f} )
134- powt = T ( 10 ) ^ f
135- quotient, remainder = fldmod (Base . widemul (x. i, y. i), powt)
143+ powt = coefficient (FD{T, f})
144+ quotient, remainder = fldmod (widemul (x. i, y. i), powt)
136145 reinterpret (FD{T, f}, _round_to_even (quotient, remainder, powt))
137146end
138147
@@ -142,14 +151,15 @@ end
142151* {T, f}(x:: FD{T, f} , y:: Integer ) = reinterpret (FD{T, f}, T (x. i * y))
143152
144153function / {T, f}(x:: FD{T, f} , y:: FD{T, f} )
145- powt = T ( 10 ) ^ f
154+ powt = coefficient (FD{T, f})
146155 quotient, remainder = fldmod (widemul (x. i, powt), y. i)
147156 reinterpret (FD{T, f}, T (_round_to_even (quotient, remainder, y. i)))
148157end
149158
150- # these functions are needed to avoid InexactError when converting from the integer type
159+ # These functions allow us to perform division with integers outside of the range of the
160+ # FixedDecimal.
151161function / {T, f}(x:: Integer , y:: FD{T, f} )
152- powt = T ( 10 ) ^ f
162+ powt = coefficient (FD{T, f})
153163 powtsq = widemul (powt, powt)
154164 quotient, remainder = fldmod (widemul (x, powtsq), y. i)
155165 reinterpret (FD{T, f}, T (_round_to_even (quotient, remainder, y. i)))
@@ -161,16 +171,17 @@ function /{T, f}(x::FD{T, f}, y::Integer)
161171end
162172
163173# integerification
164- trunc {T, f} (x:: FD{T, f} ) = FD {T, f} (div (x. i, T (10 )^ f))
165- floor {T, f} (x:: FD{T, f} ) = FD {T, f} (fld (x. i, T (10 )^ f))
174+ trunc {T, f} (x:: FD{T, f} ) = FD {T, f} (div (x. i, coefficient (FD{T, f})))
175+ floor {T, f} (x:: FD{T, f} ) = FD {T, f} (fld (x. i, coefficient (FD{T, f})))
176+
166177# TODO : round with number of digits; should be easy
167178function round {T, f} (x:: FD{T, f} , :: RoundingMode{:Nearest} = RoundNearest)
168- powt = T ( 10 ) ^ f
179+ powt = coefficient (FD{T, f})
169180 quotient, remainder = fldmod (x. i, powt)
170181 FD {T, f} (_round_to_even (quotient, remainder, powt))
171182end
172183function ceil {T, f} (x:: FD{T, f} )
173- powt = T ( 10 ) ^ f
184+ powt = coefficient (FD{T, f})
174185 quotient, remainder = fldmod (x. i, powt)
175186 if remainder > 0
176187 FD {T, f} (quotient + one (quotient))
@@ -185,49 +196,48 @@ for fn in [:trunc, :floor, :ceil]
185196 # round/trunc/ceil/flooring to FD; generic
186197 # TODO . we may need to check overflow and boundary conditions here.
187198 @eval function $fn {T, f} (:: Type{FD{T, f}} , x:: Real )
188- powt = T ( 10 ) ^ f
199+ powt = coefficient (FD{T, f})
189200 val = trunc (T, $ (Symbol (fn, " mul" ))(x, powt))
190201 reinterpret (FD{T, f}, val)
191202 end
192203end
193- round {TI <: Integer} (:: Type{TI} , x:: FD ,
194- :: RoundingMode{:Nearest} = RoundNearest) :: TI = round (x)
195- function round {T, f} ( :: Type{FD{T, f}} , x :: Real ,
196- :: RoundingMode{:Nearest} = RoundNearest)
197- reinterpret (FD{T, f}, round (T, T ( 10 ) ^ f * x ))
204+ function round {TI <: Integer} (:: Type{TI} , x:: FD , :: RoundingMode{:Nearest} = RoundNearest) :: TI
205+ round (x)
206+ end
207+ function round {T, f} ( :: Type{FD{T, f}} , x :: Real , :: RoundingMode{:Nearest} = RoundNearest)
208+ reinterpret (FD{T, f}, round (T, x * coefficient (FD{T, f}) ))
198209end
199210
200211# needed to avoid ambiguity
201- function round {T, f} (:: Type{FD{T, f}} , x:: Rational ,
202- :: RoundingMode{:Nearest} = RoundNearest)
203- reinterpret (FD{T, f}, round (T, T (10 )^ f * x))
212+ function round {T, f} (:: Type{FD{T, f}} , x:: Rational , :: RoundingMode{:Nearest} = RoundNearest)
213+ reinterpret (FD{T, f}, round (T, x * coefficient (FD{T, f})))
204214end
205215
206216# conversions and promotions
207- convert {T, f} (:: Type{FD{T, f}} , x:: Integer ) =
208- reinterpret (FD{T, f}, round (T, Base. widemul (T (x), T (10 )^ f)))
217+ function convert {T, f} (:: Type{FD{T, f}} , x:: Integer )
218+ reinterpret (FD{T, f}, T (widemul (x, coefficient (FD{T, f}))))
219+ end
209220
210221convert {T <: FD} (:: Type{T} , x:: AbstractFloat ) = round (T, x)
211222
212223function convert {T, f} (:: Type{FD{T, f}} , x:: Rational ):: FD{T, f}
213- powt = T (10 )^ f
214- num:: T , den:: T = numerator (x), denominator (x)
215- g = gcd (powt, den)
216- powt = div (powt, g)
217- den = div (den, g)
218- reinterpret (FD{T, f}, powt * num) / FD {T, f} (den)
224+ powt = coefficient (FD{T, f})
225+ reinterpret (FD{T, f}, T (x * powt))
219226end
220227
221228function convert {T, f, U, g} (:: Type{FD{T, f}} , x:: FD{U, g} )
222229 if f ≥ g
223- reinterpret (FD{T, f}, convert (T, Base. widemul (T (10 )^ (f- g), x. i)))
230+ # Compute `10^(f - g)` without overflow
231+ powt = div (coefficient (FD{T, f}), coefficient (FD{U, g}))
232+ reinterpret (FD{T, f}, T (widemul (x. i, powt)))
224233 else
225- sf = T (10 )^ (g - f)
226- q, r = divrem (x. i, sf)
227- if r ≠ 0
228- throw (InexactError ())
234+ # Compute `10^(g - f)` without overflow
235+ powt = div (coefficient (FD{U, g}), coefficient (FD{T, f}))
236+ q, r = divrem (x. i, powt)
237+ if r == 0
238+ reinterpret (FD{T, f}, T (q))
229239 else
230- reinterpret (FD{T, f}, convert (T, q ))
240+ throw ( InexactError ( ))
231241 end
232242 end
233243end
@@ -240,16 +250,20 @@ for divfn in [:div, :fld, :fld1]
240250end
241251
242252convert (:: Type{AbstractFloat} , x:: FD ) = convert (floattype (typeof (x)), x)
243- convert {TF <: AbstractFloat, T, f} (:: Type{TF} , x:: FD{T, f} ):: TF =
244- x. i / TF (10 )^ f
253+ function convert {TF <: AbstractFloat, T, f} (:: Type{TF} , x:: FD{T, f} ):: TF
254+ x. i / coefficient (FD{T, f})
255+ end
256+
257+ function convert {TF <: BigFloat, T, f} (:: Type{TF} , x:: FD{T, f} ):: TF
258+ BigInt (x. i) / BigInt (coefficient (FD{T, f}))
259+ end
245260
246261function convert {TI <: Integer, T, f} (:: Type{TI} , x:: FD{T, f} ):: TI
247262 isinteger (x) || throw (InexactError ())
248- div (x. i, T ( 10 ) ^ f )
263+ div (x. i, coefficient (FD{T, f}) )
249264end
250265
251- convert {TR<:Rational,T,f} (:: Type{TR} , x:: FD{T, f} ):: TR =
252- x. i // T (10 )^ f
266+ convert {TR <: Rational, T, f} (:: Type{TR} , x:: FD{T, f} ):: TR = x. i // coefficient (FD{T, f})
253267
254268promote_rule {T, f, TI <: Integer} (:: Type{FD{T, f}} , :: Type{TI} ) = FD{T, f}
255269promote_rule {T, f, TF <: AbstractFloat} (:: Type{FD{T, f}} , :: Type{TF} ) = TF
@@ -266,7 +280,7 @@ Base.@pure promote_rule{T, f, U, g}(::Type{FD{T, f}}, ::Type{FD{U, g}}) =
266280<= {T <: FD }(x:: T , y:: T ) = x. i <= y. i
267281
268282# predicates and traits
269- isinteger {T, f} (x:: FD{T, f} ) = rem (x. i, T ( 10 ) ^ f ) == 0
283+ isinteger {T, f} (x:: FD{T, f} ) = rem (x. i, coefficient (FD{T, f}) ) == 0
270284typemin {T, f} (:: Type{FD{T, f}} ) = reinterpret (FD{T, f}, typemin (T))
271285typemax {T, f} (:: Type{FD{T, f}} ) = reinterpret (FD{T, f}, typemax (T))
272286eps {T <: FD} (:: Type{T} ) = reinterpret (T, 1 )
@@ -283,7 +297,7 @@ function print{T, f}(io::IO, x::FD{T, f})
283297
284298 # note: a is negative if x.i == typemin(x.i)
285299 s, a = sign (x. i), abs (x. i)
286- integer, fractional = divrem (a, T ( 10 ) ^ f )
300+ integer, fractional = divrem (a, coefficient (x) )
287301 integer = abs (integer) # ...but since f > 0, this is positive
288302 fractional = abs (fractional)
289303
@@ -382,4 +396,38 @@ function parse_round{T}(::Type{T}, fractional::AbstractString, ::RoundingMode{:N
382396 return T (0 )
383397end
384398
399+
400+ """
401+ max_exp10(T)
402+
403+ The highest value of `x` which does not result in an overflow when evaluating `T(10)^x`. For
404+ types of `T` that do not overflow -1 will be returned.
405+ """
406+ function max_exp10 {T <: Integer} (:: Type{T} )
407+ applicable (typemax, T) || return - 1
408+ W = widen (T)
409+ type_max = W (typemax (T))
410+
411+ powt = one (W)
412+ ten = W (10 )
413+ exponent = 0
414+
415+ while type_max > powt
416+ powt *= ten
417+ exponent += 1
418+ end
419+
420+ exponent - 1
421+ end
422+
423+ """
424+ coefficient(::Type{FD{T, f}}) -> T
425+
426+ Compute `10^f` as an Integer without overflow. Note that overflow will not occur for any
427+ constructable `FD{T, f}`.
428+ """
429+ coefficient {T, f} (:: Type{FD{T, f}} ) = T (10 )^ f
430+ coefficient {T, f} (fd:: FD{T, f} ) = coefficient (FD{T, f})
431+ value (fd:: FD ) = fd. i
432+
385433end
0 commit comments