@@ -29,10 +29,10 @@ export FixedDecimal, RoundThrows
29
29
30
30
using Compat
31
31
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
36
36
37
37
const IEEEFloat = Union{Float16, Float32, Float64}
38
38
@@ -70,17 +70,26 @@ for fn in [:trunc, :floor, :ceil]
70
70
end
71
71
72
72
"""
73
- FixedDecimal{I <: Integer, f::Int}
73
+ FixedDecimal{T <: Integer, f::Int}
74
74
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
76
76
the decimal point stored.
77
77
"""
78
78
immutable FixedDecimal{T <: Integer , f} <: Real
79
79
i:: T
80
80
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
84
93
end
85
94
86
95
const FD = FixedDecimal
@@ -131,8 +140,8 @@ _round_to_even(q, r, d) = _round_to_even(promote(q, r, d)...)
131
140
# TODO : can we use floating point to speed this up? after we build a
132
141
# correctness test suite.
133
142
function * {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)
136
145
reinterpret (FD{T, f}, _round_to_even (quotient, remainder, powt))
137
146
end
138
147
@@ -142,14 +151,15 @@ end
142
151
* {T, f}(x:: FD{T, f} , y:: Integer ) = reinterpret (FD{T, f}, T (x. i * y))
143
152
144
153
function / {T, f}(x:: FD{T, f} , y:: FD{T, f} )
145
- powt = T ( 10 ) ^ f
154
+ powt = coefficient (FD{T, f})
146
155
quotient, remainder = fldmod (widemul (x. i, powt), y. i)
147
156
reinterpret (FD{T, f}, T (_round_to_even (quotient, remainder, y. i)))
148
157
end
149
158
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.
151
161
function / {T, f}(x:: Integer , y:: FD{T, f} )
152
- powt = T ( 10 ) ^ f
162
+ powt = coefficient (FD{T, f})
153
163
powtsq = widemul (powt, powt)
154
164
quotient, remainder = fldmod (widemul (x, powtsq), y. i)
155
165
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)
161
171
end
162
172
163
173
# 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
+
166
177
# TODO : round with number of digits; should be easy
167
178
function round {T, f} (x:: FD{T, f} , :: RoundingMode{:Nearest} = RoundNearest)
168
- powt = T ( 10 ) ^ f
179
+ powt = coefficient (FD{T, f})
169
180
quotient, remainder = fldmod (x. i, powt)
170
181
FD {T, f} (_round_to_even (quotient, remainder, powt))
171
182
end
172
183
function ceil {T, f} (x:: FD{T, f} )
173
- powt = T ( 10 ) ^ f
184
+ powt = coefficient (FD{T, f})
174
185
quotient, remainder = fldmod (x. i, powt)
175
186
if remainder > 0
176
187
FD {T, f} (quotient + one (quotient))
@@ -185,49 +196,48 @@ for fn in [:trunc, :floor, :ceil]
185
196
# round/trunc/ceil/flooring to FD; generic
186
197
# TODO . we may need to check overflow and boundary conditions here.
187
198
@eval function $fn {T, f} (:: Type{FD{T, f}} , x:: Real )
188
- powt = T ( 10 ) ^ f
199
+ powt = coefficient (FD{T, f})
189
200
val = trunc (T, $ (Symbol (fn, " mul" ))(x, powt))
190
201
reinterpret (FD{T, f}, val)
191
202
end
192
203
end
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}) ))
198
209
end
199
210
200
211
# 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})))
204
214
end
205
215
206
216
# 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
209
220
210
221
convert {T <: FD} (:: Type{T} , x:: AbstractFloat ) = round (T, x)
211
222
212
223
function 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))
219
226
end
220
227
221
228
function convert {T, f, U, g} (:: Type{FD{T, f}} , x:: FD{U, g} )
222
229
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)))
224
233
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))
229
239
else
230
- reinterpret (FD{T, f}, convert (T, q ))
240
+ throw ( InexactError ( ))
231
241
end
232
242
end
233
243
end
@@ -240,16 +250,20 @@ for divfn in [:div, :fld, :fld1]
240
250
end
241
251
242
252
convert (:: 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
245
260
246
261
function convert {TI <: Integer, T, f} (:: Type{TI} , x:: FD{T, f} ):: TI
247
262
isinteger (x) || throw (InexactError ())
248
- div (x. i, T ( 10 ) ^ f )
263
+ div (x. i, coefficient (FD{T, f}) )
249
264
end
250
265
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})
253
267
254
268
promote_rule {T, f, TI <: Integer} (:: Type{FD{T, f}} , :: Type{TI} ) = FD{T, f}
255
269
promote_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}}) =
266
280
<= {T <: FD }(x:: T , y:: T ) = x. i <= y. i
267
281
268
282
# 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
270
284
typemin {T, f} (:: Type{FD{T, f}} ) = reinterpret (FD{T, f}, typemin (T))
271
285
typemax {T, f} (:: Type{FD{T, f}} ) = reinterpret (FD{T, f}, typemax (T))
272
286
eps {T <: FD} (:: Type{T} ) = reinterpret (T, 1 )
@@ -283,7 +297,7 @@ function print{T, f}(io::IO, x::FD{T, f})
283
297
284
298
# note: a is negative if x.i == typemin(x.i)
285
299
s, a = sign (x. i), abs (x. i)
286
- integer, fractional = divrem (a, T ( 10 ) ^ f )
300
+ integer, fractional = divrem (a, coefficient (x) )
287
301
integer = abs (integer) # ...but since f > 0, this is positive
288
302
fractional = abs (fractional)
289
303
@@ -382,4 +396,38 @@ function parse_round{T}(::Type{T}, fractional::AbstractString, ::RoundingMode{:N
382
396
return T (0 )
383
397
end
384
398
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
+
385
433
end
0 commit comments