Skip to content

Commit

Permalink
Context
Browse files Browse the repository at this point in the history
  • Loading branch information
barucden committed Oct 27, 2024
1 parent 6eee6cd commit 73396bc
Show file tree
Hide file tree
Showing 7 changed files with 589 additions and 4 deletions.
1 change: 1 addition & 0 deletions src/Decimals.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ struct Decimal <: AbstractFloat
end

include("bigint.jl")
include("context.jl")

# Convert between Decimal objects, numbers, and strings
include("decimal.jl")
Expand Down
6 changes: 2 additions & 4 deletions src/arithmetic.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ Base.promote_rule(::Type{Decimal}, ::Type{<:Real}) = Decimal
Base.promote_rule(::Type{BigFloat}, ::Type{Decimal}) = Decimal
Base.promote_rule(::Type{BigInt}, ::Type{Decimal}) = Decimal

const BigTen = BigInt(10)
Base.:(+)(x::Decimal) = fixed(x)
Base.:(-)(x::Decimal) = fixed(Decimal(!x.s, x.c, x.q))

# Addition
# To add, convert both decimals to the same exponent.
Expand All @@ -24,9 +25,6 @@ function Base.:(+)(x::Decimal, y::Decimal)
return normalize(Decimal(s, abs(c), y.q))
end

# Negation
Base.:(-)(x::Decimal) = Decimal(!x.s, x.c, x.q)

# Subtraction
Base.:(-)(x::Decimal, y::Decimal) = +(x, -y)

Expand Down
2 changes: 2 additions & 0 deletions src/bigint.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ else
const libgmp = Base.GMP.libgmp
end

const BigTen = BigInt(10)

function isdivisible(x::BigInt, n::Int)
r = ccall((:__gmpz_divisible_ui_p, libgmp), Cint,
(Base.GMP.MPZ.mpz_t, Culong), x, n)
Expand Down
95 changes: 95 additions & 0 deletions src/context.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Number of significant digits
const PRECISION = Ref(9)
const ROUNDING = Ref{RoundingMode}(RoundNearest)
const EMAX = Ref(99999)
const EMIN = Ref(-99999)

function Base.setprecision(::Type{Decimal}, precision::Int)
PRECISION[] = precision
return precision
end
Base.precision(::Type{Decimal}) = PRECISION[]

function Base.setrounding(::Type{Decimal}, rounding::RoundingMode)
ROUNDING[] = rounding
return rounding
end
Base.rounding(::Type{Decimal}) = ROUNDING[]

"""
fixed(x)
Round `x` if necessary to keep within `PRECISION`.
"""
function fixed(x::Decimal)
prec = precision(Decimal)
rmod = rounding(Decimal)

Emin, Emax = EMIN[], 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

if exp_min > Etop
throw(OverflowError("Exponent limit ($Emax) exceeded: $x"))

Check warning on line 40 in src/context.jl

View check run for this annotation

Codecov / codecov/patch

src/context.jl#L40

Added line #L40 was not covered by tests
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

Check warning on line 92 in src/context.jl

View check run for this annotation

Codecov / codecov/patch

src/context.jl#L92

Added line #L92 was not covered by tests

return Decimal(signbit(c), abs(c), q)
end
2 changes: 2 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,7 @@ include("test_norm.jl")
include("test_round.jl")

include("test_compare.jl")
include("test_minus.jl")
include("test_plus.jl")

end
241 changes: 241 additions & 0 deletions test/test_minus.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
@testset "Minus" begin
setprecision(Decimal, 9)
setrounding(Decimal, RoundNearestTiesAway)
Decimals.EMAX[] = 384
Decimals.EMIN[] = -383

# minx001
@test -(dec"1") == dec"-1"
# minx002
@test -(dec"-1") == dec"1"
# minx003
@test -(dec"1.00") == dec"-1.00"
# minx004
@test -(dec"-1.00") == dec"1.00"
# minx005
@test -(dec"0") == dec"0"
# minx006
@test -(dec"0.00") == dec"0.00"
# minx007
@test -(dec"00.0") == dec"0.0"
# minx008
@test -(dec"00.00") == dec"0.00"
# minx009
@test -(dec"00") == dec"0"
# minx010
@test -(dec"-2") == dec"2"
# minx011
@test -(dec"2") == dec"-2"
# minx012
@test -(dec"-2.00") == dec"2.00"
# minx013
@test -(dec"2.00") == dec"-2.00"
# minx014
@test -(dec"-0") == dec"0"
# minx015
@test -(dec"-0.00") == dec"0.00"
# minx016
@test -(dec"-00.0") == dec"0.0"
# minx017
@test -(dec"-00.00") == dec"0.00"
# minx018
@test -(dec"-00") == dec"0"
# minx020
@test -(dec"-0e3") == dec"0e+3"
# minx021
@test -(dec"-0e2") == dec"0e+2"
# minx022
@test -(dec"-0e1") == dec"0e+1"
# minx023
@test -(dec"-0e0") == dec"0"
# minx024
@test -(dec"+0e0") == dec"0"
# minx025
@test -(dec"+0e1") == dec"0e+1"
# minx026
@test -(dec"+0e2") == dec"0e+2"
# minx027
@test -(dec"+0e3") == dec"0e+3"
# minx030
@test -(dec"-5e3") == dec"5e+3"
# minx031
@test -(dec"-5e8") == dec"5e+8"
# minx032
@test -(dec"-5e13") == dec"5e+13"
# minx033
@test -(dec"-5e18") == dec"5e+18"
# minx034
@test -(dec"+5e3") == dec"-5e+3"
# minx035
@test -(dec"+5e8") == dec"-5e+8"
# minx036
@test -(dec"+5e13") == dec"-5e+13"
# minx037
@test -(dec"+5e18") == dec"-5e+18"
# minx050
@test -(dec"-2000000") == dec"2000000"
# minx051
@test -(dec"2000000") == dec"-2000000"

setprecision(Decimal, 7)

# minx052
@test -(dec"-2000000") == dec"2000000"
# minx053
@test -(dec"2000000") == dec"-2000000"

setprecision(Decimal, 6)

# minx054
@test -(dec"-2000000") == dec"2.00000e+6"
# minx055
@test -(dec"2000000") == dec"-2.00000e+6"

setprecision(Decimal, 3)

# minx056
@test -(dec"-2000000") == dec"2.00e+6"
# minx057
@test -(dec"2000000") == dec"-2.00e+6"

setprecision(Decimal, 9)

# minx060
@test -(dec"56267e-10") == dec"-0.0000056267"
# minx061
@test -(dec"56267e-5") == dec"-0.56267"
# minx062
@test -(dec"56267e-2") == dec"-562.67"
# minx063
@test -(dec"56267e-1") == dec"-5626.7"
# minx065
@test -(dec"56267e-0") == dec"-56267"
# minx066
@test -(dec"56267e+0") == dec"-56267"
# minx067
@test -(dec"56267e+1") == dec"-5.6267e+5"
# minx068
@test -(dec"56267e+2") == dec"-5.6267e+6"
# minx069
@test -(dec"56267e+3") == dec"-5.6267e+7"
# minx070
@test -(dec"56267e+4") == dec"-5.6267e+8"
# minx071
@test -(dec"56267e+5") == dec"-5.6267e+9"
# minx072
@test -(dec"56267e+6") == dec"-5.6267e+10"
# minx080
@test -(dec"-56267e-10") == dec"0.0000056267"
# minx081
@test -(dec"-56267e-5") == dec"0.56267"
# minx082
@test -(dec"-56267e-2") == dec"562.67"
# minx083
@test -(dec"-56267e-1") == dec"5626.7"
# minx085
@test -(dec"-56267e-0") == dec"56267"
# minx086
@test -(dec"-56267e+0") == dec"56267"
# minx087
@test -(dec"-56267e+1") == dec"5.6267e+5"
# minx088
@test -(dec"-56267e+2") == dec"5.6267e+6"
# minx089
@test -(dec"-56267e+3") == dec"5.6267e+7"
# minx090
@test -(dec"-56267e+4") == dec"5.6267e+8"
# minx091
@test -(dec"-56267e+5") == dec"5.6267e+9"
# minx092
@test -(dec"-56267e+6") == dec"5.6267e+10"

Decimals.EMAX[] = 999999999
Decimals.EMIN[] = -999999999
setprecision(Decimal, 3)

# minx100
@test_throws OverflowError -(dec"9.999e+999999999")
# minx101
@test_throws OverflowError -(dec"-9.999e+999999999")

setprecision(Decimal, 3)
Decimals.EMAX[] = 999
Decimals.EMIN[] = -999

# minx110
@test -(dec"1.00e-999") == dec"-1.00e-999"
# minx111
@test -(dec"0.1e-999") == dec"-1e-1000"
# minx112
@test -(dec"0.10e-999") == dec"-1.0e-1000"
# minx113
@test -(dec"0.100e-999") == dec"-1.0e-1000"
# minx114
@test -(dec"0.01e-999") == dec"-1e-1001"
# minx115
@test -(dec"0.999e-999") == dec"-1.00e-999"
# minx116
@test -(dec"0.099e-999") == dec"-1.0e-1000"
# minx117
@test -(dec"0.009e-999") == dec"-1e-1001"
# minx118
@test -(dec"0.001e-999") == dec"-0e-1001"
# minx119
@test -(dec"0.0009e-999") == dec"-0e-1001"
# minx120
@test -(dec"0.0001e-999") == dec"-0e-1001"
# minx130
@test -(dec"-1.00e-999") == dec"1.00e-999"
# minx131
@test -(dec"-0.1e-999") == dec"1e-1000"
# minx132
@test -(dec"-0.10e-999") == dec"1.0e-1000"
# minx133
@test -(dec"-0.100e-999") == dec"1.0e-1000"
# minx134
@test -(dec"-0.01e-999") == dec"1e-1001"
# minx135
@test -(dec"-0.999e-999") == dec"1.00e-999"
# minx136
@test -(dec"-0.099e-999") == dec"1.0e-1000"
# minx137
@test -(dec"-0.009e-999") == dec"1e-1001"
# minx138
@test -(dec"-0.001e-999") == dec"0e-1001"
# minx139
@test -(dec"-0.0009e-999") == dec"0e-1001"
# minx140
@test -(dec"-0.0001e-999") == dec"0e-1001"

Decimals.EMAX[] = 999
Decimals.EMIN[] = -999
setprecision(Decimal, 9)

# minx301
@test -(dec"12345678000") == dec"-1.23456780e+10"
# minx302
@test -(dec"1234567800") == dec"-1.23456780e+9"
# minx303
@test -(dec"1234567890") == dec"-1.23456789e+9"
# minx304
@test -(dec"1234567891") == dec"-1.23456789e+9"
# minx305
@test -(dec"12345678901") == dec"-1.23456789e+10"
# minx306
@test -(dec"1234567896") == dec"-1.23456790e+9"

setprecision(Decimal, 15)

# minx321
@test -(dec"12345678000") == dec"-12345678000"
# minx322
@test -(dec"1234567800") == dec"-1234567800"
# minx323
@test -(dec"1234567890") == dec"-1234567890"
# minx324
@test -(dec"1234567891") == dec"-1234567891"
# minx325
@test -(dec"12345678901") == dec"-12345678901"
# minx326
@test -(dec"1234567896") == dec"-1234567896"
end
Loading

0 comments on commit 73396bc

Please sign in to comment.