-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This commit introduces `Context`, a structure that holds configuration of the decimal arithmetics. Eventually, the global variable `DIGITS` should be completely removed in favor of this newly-added structure.
- Loading branch information
Showing
10 changed files
with
1,732 additions
and
834 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
Base.@kwdef mutable struct Context | ||
precision::Int | ||
rounding::RoundingMode | ||
Emax::Int | ||
Emin::Int | ||
end | ||
|
||
const CONTEXT = Context(precision=28, | ||
rounding=RoundNearest, | ||
Emax=999999, | ||
Emin=-999999) | ||
|
||
function Base.setprecision(::Type{Decimal}, precision::Int) | ||
CONTEXT.precision = precision | ||
return precision | ||
end | ||
|
||
Base.precision(::Type{Decimal}) = CONTEXT.precision | ||
|
||
function Base.setrounding(::Type{Decimal}, rounding::RoundingMode) | ||
CONTEXT.rounding = rounding | ||
return rounding | ||
end | ||
|
||
Base.rounding(::Type{Decimal}) = CONTEXT.rounding | ||
|
||
""" | ||
fix(x) | ||
Round and fix the exponent of `x` to keep it within the precision and exponent | ||
limits as given by the current `CONTEXT`. | ||
""" | ||
function fix(x::Decimal) | ||
prec = precision(Decimal) | ||
rmod = rounding(Decimal) | ||
|
||
Emin, Emax = CONTEXT.Emin, CONTEXT.Emax | ||
Etiny = Emin - prec + 1 | ||
Etop = Emax - prec + 1 | ||
|
||
if iszero(x) | ||
return Decimal(x.s, x.c, clamp(x.q, Etiny, Etop)) | ||
end | ||
|
||
clen = ndigits(x.c) | ||
exp_min = clen + x.q - prec | ||
|
||
# Equivalent to `clen + x.q - 1 > Emax` | ||
if exp_min > Etop | ||
throw(OverflowError("Exponent limit ($Emax) exceeded: $x")) | ||
end | ||
|
||
subnormal = exp_min < Etiny | ||
if subnormal | ||
exp_min = Etiny | ||
end | ||
|
||
# Number of digits and exponent within bounds | ||
if x.q ≥ exp_min | ||
return x | ||
end | ||
|
||
# Signed coefficient for rounding modes like RoundToZero | ||
c = (-1)^x.s * x.c | ||
q = exp_min | ||
|
||
# Number of digits of the resulting coefficient | ||
digits = clen + x.q - exp_min | ||
if digits < 0 | ||
c = big(1) | ||
q = exp_min - 1 | ||
digits = 0 | ||
end | ||
|
||
# Number of least significant digits to remove from `c` | ||
trun_len = clen - digits | ||
|
||
# Split `c` into `digits` most significant digits and `trun_len` least | ||
# significant digits | ||
# This is like round(c, rmod, sigdigits=digits), except here we can | ||
# tell from `rem` if the rounding was lossless | ||
c, rem = divrem(c, BigTen ^ trun_len, rmod) | ||
|
||
# Rounding is exact if the truncated digits were zero | ||
exact = iszero(rem) | ||
|
||
# If the number of digits exceeded `digits` after rounding, | ||
# it means that `c` was like 99...9 and was rounded up, | ||
# becoming 100...0, so `c` is divisible by 10 | ||
if ndigits(c) > prec | ||
c = exactdiv(c, 10) | ||
q += 1 | ||
end | ||
|
||
# Exponent might have exceeded due to rounding | ||
if q > Etop | ||
throw(OverflowError("Exponent limit ($Emax) exceeded: $x")) | ||
end | ||
|
||
#if subnormal && !exact | ||
# throw(ErrorException("Underflow")) | ||
#end | ||
|
||
return Decimal(signbit(c), abs(c), q) | ||
end | ||
|
Oops, something went wrong.