Skip to content

Commit

Permalink
Merge d0fb5c2 into 18f72ce
Browse files Browse the repository at this point in the history
  • Loading branch information
lcontento committed Nov 6, 2018
2 parents 18f72ce + d0fb5c2 commit e23242b
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 30 deletions.
64 changes: 34 additions & 30 deletions src/decimal.jl
@@ -1,10 +1,40 @@
# Convert a string to a decimal, e.g. "0.01" -> Decimal(0, 1, -2)
function Base.parse(::Type{Decimal}, str::AbstractString)
if 'e' in str
return parse(Decimal, scinote(str))
# Read sign
sign = (str[1] == '-') ? 1 : 0
# Unpack scientific notation
mantissa_and_exponent = split(lowercase(str), 'e') # Both 'e' and 'E' may act as separators
# Parse exponent
exponent = if length(mantissa_and_exponent) == 1
zero(Int64)
elseif length(mantissa_and_exponent) == 2
parse(Int64, mantissa_and_exponent[2])
else
throw(ArgumentError("When parsing \"$str\" as a Decimal, more than one scientific notation exponent character was found."))
end
# Split mantissa in integer and fractional parts
mantissa = split(mantissa_and_exponent[1], '.')
integer_part = lstrip(mantissa[1], ('+', '-', '0'))
fractional_part = if length(mantissa) == 1
""
elseif length(mantissa) == 2
rstrip(mantissa[2], '0')
else
throw(ArgumentError("When parsing \"$str\" as a Decimal, more than one decimal separator was found in the mantissa."))
end
# Update exponent (move the decimal separator to the rightmost non-zero digit)
if isempty(fractional_part)
if isempty(integer_part)
return Decimal(sign, zero(BigInt), zero(Int64))
else
coefficient = rstrip(integer_part, '0') # coefficient of the Decimal number (integer)
exponent += length(integer_part) - length(coefficient)
end
else
coefficient = integer_part * fractional_part
exponent -= length(fractional_part)
end
c, q = parameters(('.' in str) ? split(str, '.') : str)
normalize(Decimal((str[1] == '-') ? 1 : 0, c, q))
Decimal(sign, abs(parse(BigInt, coefficient)), exponent)
end

decimal(str::AbstractString) = parse(Decimal, str)
Expand All @@ -18,32 +48,6 @@ Base.convert(::Type{Decimal}, num::Real) = Decimal(num::Real)
decimal(x::Real) = Decimal(x)
Decimal(x::Decimal) = x

# Get Decimal constructor parameters from string
parameters(x::AbstractString) = (abs(parse(BigInt, x)), 0)

# Get Decimal constructor parameters from array
function parameters(x::Array)
c = parse(BigInt, join(x))
(abs(c), -length(x[2]))
end

# Get decimal() argument from scientific notation
function scinote(str::AbstractString)
s = (str[1] == '-') ? "-" : ""
n, expo = split(str, 'e')
n = split(n, '.')
if s == "-"
n[1] = n[1][2:end]
end
if parse(Int64, expo) > 0
shift = parse(Int64, expo) - ((length(n) == 2) ? length(n[2]) : 0)
s * join(n) * repeat("0", shift)
else
shift = -parse(Int64, expo) - ((length(n) == 2) ? length(n[1]) : length(n))
s * "0." * repeat("0", shift) * join(n)
end
end

# Convert a decimal to a string
function Base.print(io::IO, x::Decimal)
c = string(x.c)
Expand Down
16 changes: 16 additions & 0 deletions test/test_decimal.jl
Expand Up @@ -27,6 +27,22 @@ using Compat.Test

@test parse(Decimal, "0.1234567891") == Decimal(0.1234567891) == Decimal(0,1234567891, -10)
@test parse(Decimal, "0.12345678912") == Decimal(0.12345678912) == Decimal(0,12345678912, -11)

@test parse(Decimal, "1.0000001e6") == Decimal(1.0000001e6) == Decimal(0, 10000001, -1)
@test parse(Decimal, "30e-2") == Decimal(30e-2) == Decimal(0, 3, -1)
@test parse(Decimal, "0.1234567e-15") == Decimal(0.1234567e-15) == Decimal(0, 1234567, -22)
@test parse(Decimal, "123456789.1234567899e-11") == Decimal(0, 1234567891234567899, -21)

@test parse(Decimal, "0.0") == Decimal(0) == Decimal(0.0) == Decimal(0, 0, 0)
@test parse(Decimal, "-0.0") == Decimal(-0.0) == Decimal(1, 0, 0)

@test parse(Decimal, "0012.3450") == parse(Decimal, "+0012.3450") == Decimal(12.345) == Decimal(0, 12345, -3)
@test parse(Decimal, "-0012.3450") == Decimal(-12.345) == Decimal(1, 12345, -3)
@test parse(Decimal, "0012.3450e3") == parse(Decimal, "+0012.3450e3") == Decimal(12.345e3) == Decimal(0, 12345, 0)
@test parse(Decimal, "-0012.3450e-3") == Decimal(-12.345e-3) == Decimal(1, 12345, -6)

@test_throws ArgumentError parse(Decimal, "1.2.3")
@test_throws ArgumentError parse(Decimal, "1e2e3")
end

@testset "Using `decimal`" begin
Expand Down

0 comments on commit e23242b

Please sign in to comment.