From 73396bc6dfa44671cfac26ed03a37ac87ebed687 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Denis=20Baru=C4=8Di=C4=87?= Date: Sun, 20 Oct 2024 19:44:57 +0200 Subject: [PATCH] Context --- src/Decimals.jl | 1 + src/arithmetic.jl | 6 +- src/bigint.jl | 2 + src/context.jl | 95 +++++++++++++++++ test/runtests.jl | 2 + test/test_minus.jl | 241 ++++++++++++++++++++++++++++++++++++++++++++ test/test_plus.jl | 246 +++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 589 insertions(+), 4 deletions(-) create mode 100644 src/context.jl create mode 100644 test/test_minus.jl create mode 100644 test/test_plus.jl diff --git a/src/Decimals.jl b/src/Decimals.jl index 2160794..7e8ab6c 100644 --- a/src/Decimals.jl +++ b/src/Decimals.jl @@ -21,6 +21,7 @@ struct Decimal <: AbstractFloat end include("bigint.jl") +include("context.jl") # Convert between Decimal objects, numbers, and strings include("decimal.jl") diff --git a/src/arithmetic.jl b/src/arithmetic.jl index 549c10b..f5076b5 100644 --- a/src/arithmetic.jl +++ b/src/arithmetic.jl @@ -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. @@ -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) diff --git a/src/bigint.jl b/src/bigint.jl index 08d8827..f270247 100644 --- a/src/bigint.jl +++ b/src/bigint.jl @@ -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) diff --git a/src/context.jl b/src/context.jl new file mode 100644 index 0000000..b926033 --- /dev/null +++ b/src/context.jl @@ -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")) + 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 diff --git a/test/runtests.jl b/test/runtests.jl index cf0e978..ab9ca32 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -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 diff --git a/test/test_minus.jl b/test/test_minus.jl new file mode 100644 index 0000000..097135f --- /dev/null +++ b/test/test_minus.jl @@ -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 diff --git a/test/test_plus.jl b/test/test_plus.jl new file mode 100644 index 0000000..e03d5c6 --- /dev/null +++ b/test/test_plus.jl @@ -0,0 +1,246 @@ +@testset "Plus" begin +setprecision(Decimal, 9) +setrounding(Decimal, RoundNearestTiesAway) +Decimals.EMAX[] = 384 +Decimals.EMIN[] = -383 +# plux001 +@test +(dec"1") == dec"1" +# plux002 +@test +(dec"-1") == dec"-1" +# plux003 +@test +(dec"1.00") == dec"1.00" +# plux004 +@test +(dec"-1.00") == dec"-1.00" +# plux005 +@test +(dec"0") == dec"0" +# plux006 +@test +(dec"0.00") == dec"0.00" +# plux007 +@test +(dec"00.0") == dec"0.0" +# plux008 +@test +(dec"00.00") == dec"0.00" +# plux009 +@test +(dec"00") == dec"0" +# plux010 +@test +(dec"-2") == dec"-2" +# plux011 +@test +(dec"2") == dec"2" +# plux012 +@test +(dec"-2.00") == dec"-2.00" +# plux013 +@test +(dec"2.00") == dec"2.00" +# plux014 +@test +(dec"-0") == dec"0" +# plux015 +@test +(dec"-0.00") == dec"0.00" +# plux016 +@test +(dec"-00.0") == dec"0.0" +# plux017 +@test +(dec"-00.00") == dec"0.00" +# plux018 +@test +(dec"-00") == dec"0" +# plux020 +@test +(dec"-2000000") == dec"-2000000" +# plux021 +@test +(dec"2000000") == dec"2000000" +setprecision(Decimal, 7) +# plux022 +@test +(dec"-2000000") == dec"-2000000" +# plux023 +@test +(dec"2000000") == dec"2000000" +setprecision(Decimal, 6) +# plux024 +@test +(dec"-2000000") == dec"-2.00000e+6" +# plux025 +@test +(dec"2000000") == dec"2.00000e+6" +setprecision(Decimal, 3) +# plux026 +@test +(dec"-2000000") == dec"-2.00e+6" +# plux027 +@test +(dec"2000000") == dec"2.00e+6" +setprecision(Decimal, 9) +# plux060 +@test +(dec"56267e-10") == dec"0.0000056267" +# plux061 +@test +(dec"56267e-5") == dec"0.56267" +# plux062 +@test +(dec"56267e-2") == dec"562.67" +# plux063 +@test +(dec"56267e-1") == dec"5626.7" +# plux065 +@test +(dec"56267e-0") == dec"56267" +# plux066 +@test +(dec"56267e+0") == dec"56267" +# plux067 +@test +(dec"56267e+1") == dec"5.6267e+5" +# plux068 +@test +(dec"56267e+2") == dec"5.6267e+6" +# plux069 +@test +(dec"56267e+3") == dec"5.6267e+7" +# plux070 +@test +(dec"56267e+4") == dec"5.6267e+8" +# plux071 +@test +(dec"56267e+5") == dec"5.6267e+9" +# plux072 +@test +(dec"56267e+6") == dec"5.6267e+10" +# plux080 +@test +(dec"-56267e-10") == dec"-0.0000056267" +# plux081 +@test +(dec"-56267e-5") == dec"-0.56267" +# plux082 +@test +(dec"-56267e-2") == dec"-562.67" +# plux083 +@test +(dec"-56267e-1") == dec"-5626.7" +# plux085 +@test +(dec"-56267e-0") == dec"-56267" +# plux086 +@test +(dec"-56267e+0") == dec"-56267" +# plux087 +@test +(dec"-56267e+1") == dec"-5.6267e+5" +# plux088 +@test +(dec"-56267e+2") == dec"-5.6267e+6" +# plux089 +@test +(dec"-56267e+3") == dec"-5.6267e+7" +# plux090 +@test +(dec"-56267e+4") == dec"-5.6267e+8" +# plux091 +@test +(dec"-56267e+5") == dec"-5.6267e+9" +# plux092 +@test +(dec"-56267e+6") == dec"-5.6267e+10" +# plux120 +@test +(dec"-0e3") == dec"0e+3" +# plux121 +@test +(dec"-0e2") == dec"0e+2" +# plux122 +@test +(dec"-0e1") == dec"0e+1" +# plux123 +@test +(dec"-0e0") == dec"0" +# plux124 +@test +(dec"+0e0") == dec"0" +# plux125 +@test +(dec"+0e1") == dec"0e+1" +# plux126 +@test +(dec"+0e2") == dec"0e+2" +# plux127 +@test +(dec"+0e3") == dec"0e+3" +# plux130 +@test +(dec"-5e3") == dec"-5e+3" +# plux131 +@test +(dec"-5e8") == dec"-5e+8" +# plux132 +@test +(dec"-5e13") == dec"-5e+13" +# plux133 +@test +(dec"-5e18") == dec"-5e+18" +# plux134 +@test +(dec"+5e3") == dec"5e+3" +# plux135 +@test +(dec"+5e8") == dec"5e+8" +# plux136 +@test +(dec"+5e13") == dec"5e+13" +# plux137 +@test +(dec"+5e18") == dec"5e+18" +Decimals.EMAX[] = 999999999 +Decimals.EMIN[] = -999999999 +setprecision(Decimal, 3) +# plux160 +@test_throws OverflowError +(dec"9.999e+999999999") +# plux161 +@test_throws OverflowError +(dec"-9.999e+999999999") +setprecision(Decimal, 3) +Decimals.EMAX[] = 999 +Decimals.EMIN[] = -999 +# plux210 +@test +(dec"1.00e-999") == dec"1.00e-999" +# plux211 +@test +(dec"0.1e-999") == dec"1e-1000" +# plux212 +@test +(dec"0.10e-999") == dec"1.0e-1000" +# plux213 +@test +(dec"0.100e-999") == dec"1.0e-1000" +# plux214 +@test +(dec"0.01e-999") == dec"1e-1001" +# plux215 +@test +(dec"0.999e-999") == dec"1.00e-999" +# plux216 +@test +(dec"0.099e-999") == dec"1.0e-1000" +# plux217 +@test +(dec"0.009e-999") == dec"1e-1001" +# plux218 +@test +(dec"0.001e-999") == dec"0e-1001" +# plux219 +@test +(dec"0.0009e-999") == dec"0e-1001" +# plux220 +@test +(dec"0.0001e-999") == dec"0e-1001" +# plux230 +@test +(dec"-1.00e-999") == dec"-1.00e-999" +# plux231 +@test +(dec"-0.1e-999") == dec"-1e-1000" +# plux232 +@test +(dec"-0.10e-999") == dec"-1.0e-1000" +# plux233 +@test +(dec"-0.100e-999") == dec"-1.0e-1000" +# plux234 +@test +(dec"-0.01e-999") == dec"-1e-1001" +# plux235 +@test +(dec"-0.999e-999") == dec"-1.00e-999" +# plux236 +@test +(dec"-0.099e-999") == dec"-1.0e-1000" +# plux237 +@test +(dec"-0.009e-999") == dec"-1e-1001" +# plux238 +@test +(dec"-0.001e-999") == dec"-0e-1001" +# plux239 +@test +(dec"-0.0009e-999") == dec"-0e-1001" +# plux240 +@test +(dec"-0.0001e-999") == dec"-0e-1001" +setprecision(Decimal, 16) +Decimals.EMAX[] = 384 +Decimals.EMIN[] = -383 +# plux251 +@test +(dec"7e-398") == dec"7e-398" +# plux252 +@test +(dec"0e-398") == dec"0e-398" +# plux253 +@test +(dec"7e-399") == dec"1e-398" +# plux254 +@test +(dec"4e-399") == dec"0e-398" +# plux255 +@test +(dec"7e-400") == dec"0e-398" +# plux256 +@test +(dec"7e-401") == dec"0e-398" +# plux257 +@test +(dec"0e-399") == dec"0e-398" +# plux258 +@test +(dec"0e-400") == dec"0e-398" +# plux259 +@test +(dec"0e-401") == dec"0e-398" +Decimals.EMAX[] = 999 +Decimals.EMIN[] = -999 +setprecision(Decimal, 9) +# plux301 +@test +(dec"12345678000") == dec"1.23456780e+10" +# plux302 +@test +(dec"1234567800") == dec"1.23456780e+9" +# plux303 +@test +(dec"1234567890") == dec"1.23456789e+9" +# plux304 +@test +(dec"1234567891") == dec"1.23456789e+9" +# plux305 +@test +(dec"12345678901") == dec"1.23456789e+10" +# plux306 +@test +(dec"1234567896") == dec"1.23456790e+9" +setprecision(Decimal, 15) +# plux321 +@test +(dec"12345678000") == dec"12345678000" +# plux322 +@test +(dec"1234567800") == dec"1234567800" +# plux323 +@test +(dec"1234567890") == dec"1234567890" +# plux324 +@test +(dec"1234567891") == dec"1234567891" +# plux325 +@test +(dec"12345678901") == dec"12345678901" +# plux326 +@test +(dec"1234567896") == dec"1234567896" +setprecision(Decimal, 9) +end