@@ -7,31 +7,115 @@ Base.promote_rule(::Type{BigInt}, ::Type{Decimal}) = Decimal
7
7
Base.:(+ )(x:: Decimal ) = fix (x)
8
8
Base.:(- )(x:: Decimal ) = fix (Decimal (! x. s, x. c, x. q))
9
9
10
- # Addition
11
- # To add, convert both decimals to the same exponent.
12
- # (If the exponents are different, use the smaller exponent
13
- # to make sure we're adding integers.)
14
10
function Base.:(+ )(x:: Decimal , y:: Decimal )
11
+ rmod = rounding (Decimal)
12
+
13
+ # Handle zero operands
14
+ if iszero (x) && iszero (y)
15
+ s = (x. s && y. s) || (rmod == RoundDown)
16
+ return Decimal (s, 0 , min (x. q, y. q))
17
+ elseif iszero (x) # && !iszero(y)
18
+ return + (y)
19
+ elseif iszero (y) # && !iszero(x)
20
+ return + (x)
21
+ end
22
+
23
+ # Make sure that x.q ≥ y.q
15
24
if x. q < y. q
16
25
x, y = y, x
17
26
end
18
- # Here: x.q ≥ y.q
19
- # a₁ * 10^q₁ + a₂ * 10^q₂ =
20
- # (a₁ * 10^(q₁ - q₂) + a₂) * 10^q₂
21
- # ^^^^^^^^^^^^^^^^^ this is integer because q₁ ≥ q₂
22
- q = x. q - y. q
23
- c = (- 1 )^ x. s * x. c * BigTen^ q + (- 1 )^ y. s * y. c
24
- s = signbit (c)
25
- return normalize (Decimal (s, abs (c), y. q))
27
+
28
+ # We are computing
29
+ #
30
+ # (-1)^x.s * x.c * 10^x.q + (-1)^y.s * y.c * 10^y.q =
31
+ # [(-1)^x.s * x.c * 10^(x.q - y.q) + (-1)^y.s * y.c] * 10^y.q,
32
+ #
33
+ # where `10^(x.q - y.q)` is an integer because `x.q ≥ y.q`.
34
+ #
35
+ # Sometimes, `x.q` can be much larger than `y.q`, which would result in
36
+ # a huge coefficient `x.c * 10^(x.q - y.q)`. We want to prevent that.
37
+ #
38
+ # In the end, the resulting coefficient is still constraned by precision.
39
+ # Let `Ex, Ey` denote the adjusted exponents of `x` and `y`. If
40
+ #
41
+ # Ey < min(x.q - 1, Ex - prec - 1)
42
+ # ndigits(y.c) + y.q - 1 < min(x.q - 1, ndigits(x.c) + x.q - 1 - prec - 1)
43
+ # ndigits(y.c) + y.q < x.q + min(0, ndigits(x.c) - prec - 1)
44
+ #
45
+ # then we can replace `y` by a number `z` whose adjusted exponent `E`
46
+ # satisfies the equation
47
+ #
48
+ # E = min(x.q - 1, Ex - prec - 1)
49
+ #
50
+ # and the result `x + z` equals `x + y` after rounding.
51
+ #
52
+ # From the above equation, we get that
53
+ #
54
+ # ndigits(z.c) + z.q = x.q + min(0, ndigits(x.c) - precision - 1)
55
+ #
56
+ # Setting `z.c = 1`, we have `ndigits(z.c) = 1`, and thus
57
+ #
58
+ # z.q = x.q - 1 + min(0, ndigits(x.c) - precision - 1).
59
+ #
60
+ # The problem of a huge coefficient is now gone because the difference
61
+ #
62
+ # x.q - z.q = 1 - min(0, ndigits(x.c) - precision - 1)
63
+ # = 1 + max(0, precision - ndigits(x.c) - 1)
64
+ #
65
+ # is upper-bounded by precision.
66
+
67
+ v = x. q + min (0 , ndigits (x. c) - precision (Decimal) - 1 )
68
+ if ndigits (y. c) + y. q < v
69
+ y = Decimal (y. s, BigOne, v - 1 )
70
+ end
71
+
72
+ xc = x. c * BigTen ^ (x. q - y. q)
73
+ yc = y. c
74
+ q = y. q
75
+
76
+ # The addition now amounts to
77
+ #
78
+ # ((-1)^x.s * xc + (-1)^y.s * yc) * 10^q
79
+ # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
80
+ #
81
+ # We take some extra steps to compute the addition highlighted above while
82
+ # allocating as few BigInts as possible
83
+
84
+ # If the signs are equal, we compute either `x + y` or `-x - y`, which can
85
+ # be generally written as `s * (x + y)`, where `s` is the sign of `x` and `y`
86
+ if x. s == y. s
87
+ return fix (Decimal (x. s, xc + yc, q))
88
+ end
89
+
90
+ # If the signs `x.s` and `y.s` differ, we compute either `xc - yc` or
91
+ # `yc - xc`. There are four possibilities:
92
+ #
93
+ # Signs
94
+ # x y Magnit. Result
95
+ # -----------------------------------
96
+ # + - xc > yc +(xc - yc) * 10^q
97
+ # + - xc ≤ yc -(yc - xc) * 10^q
98
+ # - + xc > yc -(xc - yc) * 10^q
99
+ # - + xc ≤ yc +(yc - xc) * 10^q
100
+ #
101
+ # If the result is zero (i.e., `xc == yc`), it should be negative if the
102
+ # rounding mode is `RoundDown` and positive otherwise.
103
+
104
+ if xc == yc
105
+ return fix (Decimal (rmod === RoundDown, BigZero, q))
106
+ elseif xc > yc
107
+ return fix (Decimal (x. s, xc - yc, q))
108
+ else
109
+ return fix (Decimal (y. s, yc - xc, q))
110
+ end
26
111
end
27
112
28
113
# Subtraction
29
114
Base.:(- )(x:: Decimal , y:: Decimal ) = + (x, - y)
30
115
31
- # Multiplication
32
116
function Base.:(* )(x:: Decimal , y:: Decimal )
33
117
s = x. s != y. s
34
- normalize (Decimal (s, BigInt ( x. c) * BigInt ( y. c) , x. q + y. q))
118
+ return fix (Decimal (s, x. c * y. c, x. q + y. q))
35
119
end
36
120
37
121
# Inversion
0 commit comments