Skip to content

Commit 514bf8a

Browse files
committed
Divide: implementation + dectest
Fixes #56 We now have: ```julia julia> Decimal(10)^30 / Decimal(2)^100 0.7888609052210118054117285655 ``` In Python: ``` >>> Decimal(10)**30 / Decimal(2)**100 Decimal('0.7888609052210118054117285655') ```
1 parent 5cd504f commit 514bf8a

File tree

6 files changed

+1243
-26
lines changed

6 files changed

+1243
-26
lines changed

scripts/dectest.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,14 @@ function print_test(io, test, directives)
108108
print(io, "@test_throws OverflowError ")
109109
print_operation(io, test.operation, test.operands)
110110
println(io)
111+
elseif :division_undefined test.conditions
112+
print(io, "@test_throws UndefinedDivisionError ")
113+
print_operation(io, test.operation, test.operands)
114+
println(io)
115+
elseif :division_by_zero test.conditions
116+
print(io, "@test_throws DivisionByZeroError ")
117+
print_operation(io, test.operation, test.operands)
118+
println(io)
111119
else
112120
print(io, "@test ")
113121
print_operation(io, test.operation, test.operands)

src/Decimals.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ module Decimals
77
export Decimal,
88
number,
99
normalize,
10-
@dec_str
10+
@dec_str,
11+
DivisionByZeroError,
12+
UndefinedDivisionError
1113

1214
const DIGITS = 20
1315

src/arithmetic.jl

Lines changed: 92 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,20 @@ Base.promote_rule(::Type{Decimal}, ::Type{<:Real}) = Decimal
44
Base.promote_rule(::Type{BigFloat}, ::Type{Decimal}) = Decimal
55
Base.promote_rule(::Type{BigInt}, ::Type{Decimal}) = Decimal
66

7+
"""
8+
DivisionByZeroError
9+
10+
Division was attempted with a denominator value of 0.
11+
"""
12+
struct DivisionByZeroError <: Exception end
13+
14+
"""
15+
UndefinedDivisionError
16+
17+
Division was attempted with both numerator and denominator value of 0.
18+
"""
19+
struct UndefinedDivisionError <: Exception end
20+
721
Base.:(+)(x::Decimal) = fix(x)
822
Base.:(-)(x::Decimal) = fix(Decimal(!x.s, x.c, x.q))
923

@@ -121,15 +135,86 @@ function Base.:(*)(x::Decimal, y::Decimal)
121135
return fix(Decimal(s, x.c * y.c, x.q + y.q))
122136
end
123137

124-
# Inversion
125-
function Base.inv(x::Decimal)
126-
c = round(BigInt, BigInt(10)^(-x.q + DIGITS) / x.c) # the decimal point of 1/x.c is shifted by -x.q so that the integer part of the result is correct and then it is shifted further by DIGITS to also cover some digits from the fractional part.
127-
q = -DIGITS # we only need to remember that there are these digits after the decimal point
128-
normalize(Decimal(x.s, c, q))
138+
function Base.:(/)(x::Decimal, y::Decimal)
139+
if iszero(y)
140+
if iszero(x)
141+
throw(UndefinedDivisionError())
142+
else
143+
throw(DivisionByZeroError())
144+
end
145+
end
146+
147+
s = x.s != y.s
148+
149+
# 0 / y, where y ≠ 0
150+
if iszero(x)
151+
return Decimal(s, BigZero, x.q - y.q)
152+
end
153+
154+
prec = precision(Decimal)
155+
156+
# We are computing
157+
#
158+
# (x.c * 10^x.q) / (y.c * 10^y.q)
159+
# = (x.c / y.c) * 10^(x.q - y.q).
160+
#
161+
# The coefficient `x.c / y.c` is not an integer in general.
162+
# We could just compute the division, resulting in a BigFloat, and round
163+
# the result. However, we would have to use `setprecision` to ensure the
164+
# BigFloat has enough precision. To avoid that, we compute the division
165+
# with a sufficient precision ourselves.
166+
#
167+
# We write the division as
168+
#
169+
# (x.c / y.c) * 10^(x.q - y.q) * 10^(d - d)
170+
# = ((x.c * 10^d) / y.c) * 10^(x.q - y.q - d) (P1)
171+
# = (x.c / (y.c * 10^(-d))) * 10^(x.q - y.q - d), (P2)
172+
#
173+
# where `d` is any integer, which we use to increase the dividend `x.c` (if
174+
# `d ≥ 0`) or the divisor `y.c` (if `d < 0`) before performing integer
175+
# division so that the result has required precision.
176+
#
177+
# Assume that `x.c * 10^d` has `m + d` digits and `y.c` has `n` digits,
178+
# which means
179+
#
180+
# 10^(m + d - 1) ≤ x < 10^(m + d)
181+
# 10^(n - 1) ≤ y < 10^n.
182+
#
183+
# We can thus bound the quotient `(x.c * 10^d) / y.c`:
184+
#
185+
# 10^(m + d - 1) / 10^n < x/y < 10^(m + d) / 10^(n - 1)
186+
# 10^(m + d - n - 1) < x/y < 10^(m + d - n + 1)
187+
#
188+
# We set `d` so that the lower-bound has `prec + 1` digits (i.e., it is
189+
# equal to `10^prec`):
190+
#
191+
# 10^(m + d - n - 1) = 10^prec
192+
# d = prec - m + n + 1
193+
#
194+
# If the lower-bound has `prec + 1` digits, it means that any quotient has
195+
# at least that amount of digits. We need `prec + 1` (instead of `prec`)
196+
# digits because the least-significant digit is needed for correct
197+
# rounding (done by the `fix` function).
198+
199+
d = prec - ndigits(x.c) + ndigits(y.c) + 1
200+
q = x.q - y.q - d
201+
202+
if d > 0
203+
# If `d` is positive, we take the path denoted (P1)
204+
c = div(x.c * BigTen^d, y.c)
205+
elseif d < 0
206+
# If `d` is negative, we take the path denoted (P2)
207+
c = div(x.c, y.c * BigTen^(-d))
208+
else
209+
c = div(x.c, y.c)
210+
end
211+
212+
return fix(Decimal(s, c, q))
129213
end
130214

131-
# Division
132-
Base.:(/)(x::Decimal, y::Decimal) = x * inv(y)
215+
function Base.inv(x::Decimal)
216+
return Decimal(false, BigOne, 0) / x
217+
end
133218

134219
Base.abs(x::Decimal) = fix(Decimal(false, x.c, x.q))
135220

0 commit comments

Comments
 (0)