Skip to content

Commit e8c9f3b

Browse files
authored
Merge pull request #52 from JuliaMath/nhd-explicit-base-extending
Explicitly extend Base functions (Base.foo(...) = ...) instead of imports.
2 parents 81ca0e0 + a2b442e commit e8c9f3b

File tree

1 file changed

+74
-71
lines changed

1 file changed

+74
-71
lines changed

src/FixedPointDecimals.jl

Lines changed: 74 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,7 @@ module FixedPointDecimals
2727

2828
export FixedDecimal, RoundThrows
2929

30-
import Base: reinterpret, zero, one, abs, sign, ==, <, <=, +, -, /, *, div, rem, divrem,
31-
fld, mod, fldmod, fld1, mod1, fldmod1, isinteger, typemin, typemax,
32-
print, show, string, convert, parse, promote_rule, min, max,
33-
floatmin, floatmax, trunc, round, floor, ceil, eps, float, widemul, decompose
34-
35-
const BitInteger = Union{Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64,
36-
UInt64, Int128, UInt128}
30+
using Base: decompose, BitInteger, @pure
3731

3832
# floats that support fma and are roughly IEEE-like
3933
const FMAFloat = Union{Float16, Float32, Float64, BigFloat}
@@ -85,8 +79,8 @@ struct FixedDecimal{T <: Integer, f} <: Real
8579
i::T
8680

8781
# inner constructor
88-
# This function is marked as `Base.@pure`. It does not have or depend on any side-effects.
89-
Base.@pure function Base.reinterpret(::Type{FixedDecimal{T, f}}, i::Integer) where {T, f}
82+
# This function is marked as `@pure`. It does not have or depend on any side-effects.
83+
@pure function Base.reinterpret(::Type{FixedDecimal{T, f}}, i::Integer) where {T, f}
9084
n = max_exp10(T)
9185
if f >= 0 && (n < 0 || f <= n)
9286
new{T, f}(i % T)
@@ -114,22 +108,22 @@ floattype(::Type{<:FD{T}}) where {T<:Integer} = Float64
114108
floattype(::Type{<:FD{BigInt}}) = BigFloat
115109

116110
# basic operators
117-
-(x::FD{T, f}) where {T, f} = reinterpret(FD{T, f}, -x.i)
118-
abs(x::FD{T, f}) where {T, f} = reinterpret(FD{T, f}, abs(x.i))
111+
Base.:-(x::FD{T, f}) where {T, f} = reinterpret(FD{T, f}, -x.i)
112+
Base.abs(x::FD{T, f}) where {T, f} = reinterpret(FD{T, f}, abs(x.i))
119113

120-
+(x::FD{T, f}, y::FD{T, f}) where {T, f} = reinterpret(FD{T, f}, x.i+y.i)
121-
-(x::FD{T, f}, y::FD{T, f}) where {T, f} = reinterpret(FD{T, f}, x.i-y.i)
114+
Base.:+(x::FD{T, f}, y::FD{T, f}) where {T, f} = reinterpret(FD{T, f}, x.i+y.i)
115+
Base.:-(x::FD{T, f}, y::FD{T, f}) where {T, f} = reinterpret(FD{T, f}, x.i-y.i)
122116

123117
# wide multiplication
124-
Base.@pure function widemul(x::FD{<:Any, f}, y::FD{<:Any, g}) where {f, g}
118+
@pure function Base.widemul(x::FD{<:Any, f}, y::FD{<:Any, g}) where {f, g}
125119
i = widemul(x.i, y.i)
126120
reinterpret(FD{typeof(i), f + g}, i)
127121
end
128-
Base.@pure function widemul(x::FD{T, f}, y::Integer) where {T, f}
122+
@pure function Base.widemul(x::FD{T, f}, y::Integer) where {T, f}
129123
i = widemul(x.i, y)
130124
reinterpret(FD{typeof(i), f}, i)
131125
end
132-
Base.@pure widemul(x::Integer, y::FD) = widemul(y, x)
126+
@pure Base.widemul(x::Integer, y::FD) = widemul(y, x)
133127

134128
"""
135129
_round_to_even(quotient, remainder, divisor)
@@ -160,48 +154,48 @@ _round_to_even(q, r, d) = _round_to_even(promote(q, r, d)...)
160154
# multiplication rounds to nearest even representation
161155
# TODO: can we use floating point to speed this up? after we build a
162156
# correctness test suite.
163-
function *(x::FD{T, f}, y::FD{T, f}) where {T, f}
157+
function Base.:*(x::FD{T, f}, y::FD{T, f}) where {T, f}
164158
powt = coefficient(FD{T, f})
165159
quotient, remainder = fldmodinline(widemul(x.i, y.i), powt)
166160
reinterpret(FD{T, f}, _round_to_even(quotient, remainder, powt))
167161
end
168162

169163
# these functions are needed to avoid InexactError when converting from the
170164
# integer type
171-
*(x::Integer, y::FD{T, f}) where {T, f} = reinterpret(FD{T, f}, T(x * y.i))
172-
*(x::FD{T, f}, y::Integer) where {T, f} = reinterpret(FD{T, f}, T(x.i * y))
165+
Base.:*(x::Integer, y::FD{T, f}) where {T, f} = reinterpret(FD{T, f}, T(x * y.i))
166+
Base.:*(x::FD{T, f}, y::Integer) where {T, f} = reinterpret(FD{T, f}, T(x.i * y))
173167

174-
function /(x::FD{T, f}, y::FD{T, f}) where {T, f}
168+
function Base.:/(x::FD{T, f}, y::FD{T, f}) where {T, f}
175169
powt = coefficient(FD{T, f})
176170
quotient, remainder = fldmod(widemul(x.i, powt), y.i)
177171
reinterpret(FD{T, f}, T(_round_to_even(quotient, remainder, y.i)))
178172
end
179173

180174
# These functions allow us to perform division with integers outside of the range of the
181175
# FixedDecimal.
182-
function /(x::Integer, y::FD{T, f}) where {T, f}
176+
function Base.:/(x::Integer, y::FD{T, f}) where {T, f}
183177
powt = coefficient(FD{T, f})
184178
powtsq = widemul(powt, powt)
185179
quotient, remainder = fldmod(widemul(x, powtsq), y.i)
186180
reinterpret(FD{T, f}, T(_round_to_even(quotient, remainder, y.i)))
187181
end
188182

189-
function /(x::FD{T, f}, y::Integer) where {T, f}
183+
function Base.:/(x::FD{T, f}, y::Integer) where {T, f}
190184
quotient, remainder = fldmod(x.i, y)
191185
reinterpret(FD{T, f}, T(_round_to_even(quotient, remainder, y)))
192186
end
193187

194188
# integerification
195-
trunc(x::FD{T, f}) where {T, f} = FD{T, f}(div(x.i, coefficient(FD{T, f})))
196-
floor(x::FD{T, f}) where {T, f} = FD{T, f}(fld(x.i, coefficient(FD{T, f})))
189+
Base.trunc(x::FD{T, f}) where {T, f} = FD{T, f}(div(x.i, coefficient(FD{T, f})))
190+
Base.floor(x::FD{T, f}) where {T, f} = FD{T, f}(fld(x.i, coefficient(FD{T, f})))
197191

198192
# TODO: round with number of digits; should be easy
199-
function round(x::FD{T, f}, ::RoundingMode{:Nearest}=RoundNearest) where {T, f}
193+
function Base.round(x::FD{T, f}, ::RoundingMode{:Nearest}=RoundNearest) where {T, f}
200194
powt = coefficient(FD{T, f})
201195
quotient, remainder = fldmodinline(x.i, powt)
202196
FD{T, f}(_round_to_even(quotient, remainder, powt))
203197
end
204-
function ceil(x::FD{T, f}) where {T, f}
198+
function Base.ceil(x::FD{T, f}) where {T, f}
205199
powt = coefficient(FD{T, f})
206200
quotient, remainder = fldmodinline(x.i, powt)
207201
if remainder > 0
@@ -243,44 +237,44 @@ end
243237
_apply_exact_float(f, ::Type{T}, x::Real, i::Integer) where T = f(T, x, i)
244238

245239
for fn in [:trunc, :floor, :ceil]
246-
@eval ($fn(::Type{TI}, x::FD)::TI) where {TI <: Integer} = $fn(x)
240+
@eval (Base.$fn(::Type{TI}, x::FD)::TI) where {TI <: Integer} = $fn(x)
247241

248242
# round/trunc/ceil/flooring to FD; generic
249-
@eval function $fn(::Type{FD{T, f}}, x::Real) where {T, f}
243+
@eval function Base.$fn(::Type{FD{T, f}}, x::Real) where {T, f}
250244
powt = coefficient(FD{T, f})
251245
# Use machine Float64 if possible, but fall back to BigFloat if we need
252246
# more precision. 4f bits suffices.
253247
val = _apply_exact_float($(Symbol(fn, "mul")), T, x, powt)
254248
reinterpret(FD{T, f}, val)
255249
end
256250
end
257-
function round(::Type{TI}, x::FD, ::RoundingMode{:Nearest}=RoundNearest) where {TI <: Integer}
251+
function Base.round(::Type{TI}, x::FD, ::RoundingMode{:Nearest}=RoundNearest) where {TI <: Integer}
258252
convert(TI, round(x))::TI
259253
end
260-
function round(::Type{FD{T, f}}, x::Real, ::RoundingMode{:Nearest}=RoundNearest) where {T, f}
254+
function Base.round(::Type{FD{T, f}}, x::Real, ::RoundingMode{:Nearest}=RoundNearest) where {T, f}
261255
reinterpret(FD{T, f}, round(T, x * coefficient(FD{T, f})))
262256
end
263257

264258
# needed to avoid ambiguity
265-
function round(::Type{FD{T, f}}, x::Rational, ::RoundingMode{:Nearest}=RoundNearest) where {T, f}
259+
function Base.round(::Type{FD{T, f}}, x::Rational, ::RoundingMode{:Nearest}=RoundNearest) where {T, f}
266260
reinterpret(FD{T, f}, round(T, x * coefficient(FD{T, f})))
267261
end
268262

269263
# conversions and promotions
270-
convert(::Type{FD{T, f}}, x::FD{T, f}) where {T, f} = x # Converting an FD to itself is a no-op
264+
Base.convert(::Type{FD{T, f}}, x::FD{T, f}) where {T, f} = x # Converting an FD to itself is a no-op
271265

272-
function convert(::Type{FD{T, f}}, x::Integer) where {T, f}
266+
function Base.convert(::Type{FD{T, f}}, x::Integer) where {T, f}
273267
reinterpret(FD{T, f}, T(widemul(x, coefficient(FD{T, f}))))
274268
end
275269

276-
convert(::Type{T}, x::AbstractFloat) where {T <: FD} = round(T, x)
270+
Base.convert(::Type{T}, x::AbstractFloat) where {T <: FD} = round(T, x)
277271

278-
function convert(::Type{FD{T, f}}, x::Rational) where {T, f}
272+
function Base.convert(::Type{FD{T, f}}, x::Rational) where {T, f}
279273
powt = coefficient(FD{T, f})
280274
reinterpret(FD{T, f}, T(x * powt))::FD{T, f}
281275
end
282276

283-
function convert(::Type{FD{T, f}}, x::FD{U, g}) where {T, f, U, g}
277+
function Base.convert(::Type{FD{T, f}}, x::FD{U, g}) where {T, f, U, g}
284278
if f g
285279
# Compute `10^(f - g)` without overflow
286280
powt = div(coefficient(FD{T, f}), coefficient(FD{U, g}))
@@ -298,7 +292,7 @@ function convert(::Type{FD{T, f}}, x::FD{U, g}) where {T, f, U, g}
298292
end
299293

300294
for remfn in [:rem, :mod, :mod1, :min, :max]
301-
@eval $remfn(x::T, y::T) where {T <: FD} = reinterpret(T, $remfn(x.i, y.i))
295+
@eval Base.$remfn(x::T, y::T) where {T <: FD} = reinterpret(T, $remfn(x.i, y.i))
302296
end
303297
# TODO: When we upgrade to a min julia version >=1.4 (i.e Julia 2.0), this block can be
304298
# dropped in favor of three-argument `div`, below.
@@ -313,33 +307,33 @@ if VERSION >= v"1.4.0-"
313307
Base.div(x::T, y::T, r::RoundingMode) where {T <: FD} = T(div(x.i, y.i, r))
314308
end
315309

316-
convert(::Type{AbstractFloat}, x::FD) = convert(floattype(typeof(x)), x)
317-
function convert(::Type{TF}, x::FD{T, f}) where {TF <: AbstractFloat, T, f}
310+
Base.convert(::Type{AbstractFloat}, x::FD) = convert(floattype(typeof(x)), x)
311+
function Base.convert(::Type{TF}, x::FD{T, f}) where {TF <: AbstractFloat, T, f}
318312
convert(TF, x.i / coefficient(FD{T, f}))::TF
319313
end
320314

321-
function convert(::Type{TF}, x::FD{T, f}) where {TF <: BigFloat, T, f}
315+
function Base.convert(::Type{TF}, x::FD{T, f}) where {TF <: BigFloat, T, f}
322316
convert(TF, BigInt(x.i) / BigInt(coefficient(FD{T, f})))::TF
323317
end
324318

325-
function convert(::Type{TI}, x::FD{T, f}) where {TI <: Integer, T, f}
319+
function Base.convert(::Type{TI}, x::FD{T, f}) where {TI <: Integer, T, f}
326320
isinteger(x) || throw(InexactError(:convert, TI, x))
327321
convert(TI, div(x.i, coefficient(FD{T, f})))::TI
328322
end
329323

330-
function convert(::Type{TR}, x::FD{T, f}) where {TR <: Rational, T, f}
324+
function Base.convert(::Type{TR}, x::FD{T, f}) where {TR <: Rational, T, f}
331325
convert(TR, x.i // coefficient(FD{T, f}))::TR
332326
end
333327

334328
(::Type{T})(x::FD) where {T<:Union{AbstractFloat,Integer,Rational}} = convert(T, x)
335329

336-
promote_rule(::Type{FD{T, f}}, ::Type{<:Integer}) where {T, f} = FD{T, f}
337-
promote_rule(::Type{<:FD}, ::Type{TF}) where {TF <: AbstractFloat} = TF
338-
promote_rule(::Type{<:FD}, ::Type{Rational{TR}}) where {TR} = Rational{TR}
330+
Base.promote_rule(::Type{FD{T, f}}, ::Type{<:Integer}) where {T, f} = FD{T, f}
331+
Base.promote_rule(::Type{<:FD}, ::Type{TF}) where {TF <: AbstractFloat} = TF
332+
Base.promote_rule(::Type{<:FD}, ::Type{Rational{TR}}) where {TR} = Rational{TR}
339333

340334
# TODO: decide if these are the right semantics;
341335
# right now we pick the bigger int type and the bigger decimal point
342-
Base.@pure function promote_rule(::Type{FD{T, f}}, ::Type{FD{U, g}}) where {T, f, U, g}
336+
@pure function Base.promote_rule(::Type{FD{T, f}}, ::Type{FD{U, g}}) where {T, f, U, g}
343337
FD{promote_type(T, U), max(f, g)}
344338
end
345339

@@ -348,24 +342,24 @@ Base.zero(::Type{FD{T, f}}) where {T, f} = reinterpret(FD{T, f}, zero(T))
348342
Base.one(::Type{FD{T, f}}) where {T, f} = reinterpret(FD{T, f}, coefficient(FD{T, f}))
349343

350344
# comparison
351-
==(x::T, y::T) where {T <: FD} = x.i == y.i
352-
<(x::T, y::T) where {T <: FD} = x.i < y.i
353-
<=(x::T, y::T) where {T <: FD} = x.i <= y.i
345+
Base.:(==)(x::T, y::T) where {T <: FD} = x.i == y.i
346+
Base.:(<)(x::T, y::T) where {T <: FD} = x.i < y.i
347+
Base.:(<=)(x::T, y::T) where {T <: FD} = x.i <= y.i
354348

355349
# predicates and traits
356-
isinteger(x::FD{T, f}) where {T, f} = rem(x.i, coefficient(FD{T, f})) == 0
357-
typemin(::Type{FD{T, f}}) where {T, f} = reinterpret(FD{T, f}, typemin(T))
358-
typemax(::Type{FD{T, f}}) where {T, f}= reinterpret(FD{T, f}, typemax(T))
359-
eps(::Type{T}) where {T <: FD} = reinterpret(T, 1)
360-
eps(x::FD) = eps(typeof(x))
361-
floatmin(::Type{T}) where {T <: FD} = eps(T)
362-
floatmax(::Type{T}) where {T <: FD} = typemax(T)
350+
Base.isinteger(x::FD{T, f}) where {T, f} = rem(x.i, coefficient(FD{T, f})) == 0
351+
Base.typemin(::Type{FD{T, f}}) where {T, f} = reinterpret(FD{T, f}, typemin(T))
352+
Base.typemax(::Type{FD{T, f}}) where {T, f}= reinterpret(FD{T, f}, typemax(T))
353+
Base.eps(::Type{T}) where {T <: FD} = reinterpret(T, 1)
354+
Base.eps(x::FD) = eps(typeof(x))
355+
Base.floatmin(::Type{T}) where {T <: FD} = eps(T)
356+
Base.floatmax(::Type{T}) where {T <: FD} = typemax(T)
363357

364358
# printing
365-
function print(io::IO, x::FD{T, 0}) where T
359+
function Base.print(io::IO, x::FD{T, 0}) where T
366360
print(io, x.i)
367361
end
368-
function print(io::IO, x::FD{T, f}) where {T, f}
362+
function Base.print(io::IO, x::FD{T, f}) where {T, f}
369363
iscompact = get(io, :compact, false)
370364

371365
# note: a is negative if x.i == typemin(x.i)
@@ -387,7 +381,7 @@ function print(io::IO, x::FD{T, f}) where {T, f}
387381
print(io, integer, '.', fractionchars)
388382
end
389383

390-
function show(io::IO, x::FD{T, f}) where {T, f}
384+
function Base.show(io::IO, x::FD{T, f}) where {T, f}
391385
iscompact = get(io, :compact, false)
392386
if !iscompact
393387
print(io, "FixedDecimal{$T,$f}(")
@@ -407,7 +401,7 @@ Raises an `InexactError` if any rounding is necessary.
407401
"""
408402
const RoundThrows = RoundingMode{:Throw}()
409403

410-
function parse(::Type{FD{T, f}}, str::AbstractString, mode::RoundingMode=RoundNearest) where {T, f}
404+
function Base.parse(::Type{FD{T, f}}, str::AbstractString, mode::RoundingMode=RoundNearest) where {T, f}
411405
if !(mode in [RoundThrows, RoundNearest, RoundToZero])
412406
throw(ArgumentError("Unhandled rounding mode $mode"))
413407
end
@@ -475,12 +469,17 @@ end
475469
476470
The highest value of `x` which does not result in an overflow when evaluating `T(10)^x`. For
477471
types of `T` that do not overflow -1 will be returned.
478-
"""
479-
Base.@pure function max_exp10(::Type{T}) where {T <: Integer}
480-
# This function is marked as `Base.@pure`. Even though it does call some generic
481-
# functions, they are all simple methods that should be able to be evaluated as
482-
# constants. This function does not have or depend on any side-effects.
483472
473+
NOTE: This function is expensive, since it contains a while-loop, but it is actually
474+
computing a constant value for types, so it really only needs to be run once per type.
475+
We achieve this by `@eval`ing new methods in a loop, below. Users can do this
476+
themselves to add more "frozen" methods for custom Integer types:
477+
```julia
478+
@eval FixedPointDecimals.max_exp10(::Type{CustomIntType}) = \$(max_exp10(CustomIntType))
479+
```
480+
This function does not have or depend on any side-effects.
481+
"""
482+
function max_exp10(::Type{T}) where {T <: Integer}
484483
W = widen(T)
485484
type_max = W(typemax(T))
486485

@@ -497,9 +496,13 @@ Base.@pure function max_exp10(::Type{T}) where {T <: Integer}
497496
end
498497

499498
max_exp10(::Type{BigInt}) = -1
500-
# Freeze the evaluation for Int128, since max_exp10(Int128) is too compilicated to get
501-
# optimized away by the compiler during const-folding.
502-
@eval max_exp10(::Type{Int128}) = $(max_exp10(Int128))
499+
500+
# Freeze the evaluation for BitInteger types, since max_exp10() is too compilicated to get
501+
# optimized away by the compiler during const-folding. (We can't freeze for user-defined
502+
# types because we don't know what they are yet.)
503+
for T in Base.BitInteger_types
504+
@eval max_exp10(::Type{$T}) = $(max_exp10(T))
505+
end
503506

504507
# coefficient is marked pure. This is needed to ensure that the result is always available
505508
# at compile time, and can therefore be used when optimizing mathematical operations.
@@ -509,11 +512,11 @@ max_exp10(::Type{BigInt}) = -1
509512
Compute `10^f` as an Integer without overflow. Note that overflow will not occur for any
510513
constructable `FD{T, f}`.
511514
"""
512-
Base.@pure coefficient(::Type{FD{T, f}}) where {T, f} = T(10)^f
513-
Base.@pure coefficient(fd::FD{T, f}) where {T, f} = coefficient(FD{T, f})
515+
@pure coefficient(::Type{FD{T, f}}) where {T, f} = T(10)^f
516+
@pure coefficient(fd::FD{T, f}) where {T, f} = coefficient(FD{T, f})
514517
value(fd::FD) = fd.i
515518

516519
# for generic hashing
517-
decompose(fd::FD) = decompose(Rational(fd))
520+
Base.decompose(fd::FD) = decompose(Rational(fd))
518521

519522
end

0 commit comments

Comments
 (0)