|
1 |
| -# Equality |
| 1 | +_sign(x::Decimal) = x.s ? -1 : 1 |
2 | 2 |
|
3 |
| -# equals() now depends on == instead |
4 |
| -# of the other way round. |
5 |
| -function Base.:(==)(x::Decimal, y::Decimal) |
6 |
| - # return early on zero |
7 |
| - x_is_zero = iszero(x) |
8 |
| - y_is_zero = iszero(y) |
9 |
| - if x_is_zero || y_is_zero |
10 |
| - return x_is_zero === y_is_zero |
| 3 | +function Base.cmp(x::Decimal, y::Decimal) |
| 4 | + if iszero(x) && iszero(y) |
| 5 | + return 0 |
| 6 | + elseif iszero(x) # && !iszero(y) |
| 7 | + return -_sign(y) |
| 8 | + elseif iszero(y) # && !iszero(x) |
| 9 | + return _sign(x) |
11 | 10 | end
|
12 | 11 |
|
13 |
| - a = normalize(x) |
14 |
| - b = normalize(y) |
15 |
| - a.c == b.c && a.q == b.q && a.s == b.s |
16 |
| -end |
| 12 | + # Neither x nor y is zero here |
17 | 13 |
|
18 |
| -function Base.:(<)(x::Decimal, y::Decimal) |
19 |
| - # return early on zero |
20 |
| - if iszero(x) && iszero(y) |
21 |
| - return false |
| 14 | + if x.s != y.s |
| 15 | + # x and y have different signs, so |
| 16 | + # if x < 0, then return -1 (because y is positive) |
| 17 | + # if x > 0, then return +1 (because y is negative) |
| 18 | + return _sign(x) |
22 | 19 | end
|
23 | 20 |
|
24 |
| - # avoid normalization if possible |
25 |
| - if x.q == y.q |
26 |
| - return isless(x.s == 0 ? x.c : -x.c, y.s == 0 ? y.c : -y.c) |
| 21 | + cmp_c = cmp(x.c, y.c) |
| 22 | + cmp_q = cmp(x.q, y.q) |
| 23 | + |
| 24 | + # If both x.c and x.q is greater (or equal, or less) than y.c and y.q, |
| 25 | + # then x is greater (or equal, or less) than y |
| 26 | + if cmp_c == cmp_q |
| 27 | + return cmp_c |
27 | 28 | end
|
28 | 29 |
|
29 |
| - diff = y - x |
| 30 | + # Adjusted exponent of x and y |
| 31 | + # It is the position of the most significant digit with respect to |
| 32 | + # the decimal point |
| 33 | + expx = ndigits(x.c) + x.q - 1 |
| 34 | + expy = ndigits(y.c) + y.q - 1 |
30 | 35 |
|
31 |
| - farther_from_0 = diff.c > 0 || (iszero(diff.c) && diff.q > 0) |
| 36 | + # If expx > expy, then abs(x) > abs(y) |
| 37 | + # If expx < expy, then abs(x) < abs(y) |
| 38 | + # |
| 39 | + # Then we need to consider the sign, which is the same for x and y here |
| 40 | + # |
| 41 | + # Overall: |
| 42 | + # -1 if expx > expy and they are negative |
| 43 | + # +1 if expx > expy and they are positive |
| 44 | + # -1 if expx < expy and they are positive |
| 45 | + # +1 if expx < expy and they are negative |
| 46 | + if expx != expy |
| 47 | + s = _sign(x) # same as _sign(y) |
| 48 | + return ifelse(expx > expy, s, -s) |
| 49 | + end |
32 | 50 |
|
33 |
| - if diff.s == 1 |
34 |
| - return !farther_from_0 |
| 51 | + # cmp(x, y) = sign(x - y) |
| 52 | + # = sign(sign(x) * abs(x) - sign(y) * abs(y)) |
| 53 | + # |
| 54 | + # We know that x and y have the same sign here: |
| 55 | + # |
| 56 | + # cmp(x, y) = sign(sign(x) * (abs(x) - abs(y))) |
| 57 | + # = sign(x) * sign(abs(x) - abs(y)) |
| 58 | + # = sign(x) * sign(x.c * 10^x.q - y.c * 10^y.q) |
| 59 | + # |
| 60 | + # Now, for the latter sign: |
| 61 | + # |
| 62 | + # sign(x.c * 10^x.q - y.c * 10^y.q) |
| 63 | + # = sign(x.c * 10^(x.q - y.q) - y.c) * 10^y.q |
| 64 | + # = sign(x.c - y.c * 10^(y.q - x.q)) * 10^x.q |
| 65 | + # ^^^^^^ positive |
| 66 | + # |
| 67 | + # So, we just need to return |
| 68 | + # |
| 69 | + # sign(x) * sign(x.c * 10^(x.q - y.q) - y.c) if x.q ≥ y.q, |
| 70 | + # sign(x) * sign(x.c - y.c * 10^(y.q - x.q)) if x.q < y.q |
| 71 | + if x.q ≥ y.q |
| 72 | + q = x.q - y.q |
| 73 | + return _sign(x) * cmp(x.c * big(10) ^ q, y.c) |
35 | 74 | else
|
36 |
| - return farther_from_0 |
| 75 | + q = y.q - x.q |
| 76 | + return _sign(x) * cmp(x.c, y.c * big(10) ^ q) |
37 | 77 | end
|
38 | 78 | end
|
39 | 79 |
|
40 |
| -function Base.:(<=)(x::Decimal, y::Decimal) |
41 |
| - return x < y || x == y |
42 |
| -end |
| 80 | +Base.:(==)(x::Decimal, y::Decimal) = iszero(cmp(x, y)) |
| 81 | +Base.:(<)(x::Decimal, y::Decimal) = cmp(x, y) < 0 |
| 82 | +Base.:(<=)(x::Decimal, y::Decimal) = cmp(x, y) <= 0 |
43 | 83 |
|
44 | 84 | # Special case equality with AbstractFloat to allow comparison against Inf/Nan
|
45 | 85 | # which are not representable in Decimal
|
|
0 commit comments