|
| 1 | +""" |
| 2 | +Decs package |
| 3 | +
|
| 4 | +Copyright 2019 Gandalf Software, Inc., Scott P. Jones |
| 5 | +Licensed under MIT License, see LICENSE.md |
| 6 | +Originally based/inspired by Decimals.jl (by [email protected] (Jack Peterson), 7/3/2014) |
| 7 | +""" |
| 8 | +module Decs |
| 9 | + |
| 10 | +macro dec_str(s, flags...) parse(Dec, s) end |
| 11 | + |
| 12 | +import Base: ==, +, -, *, /, <, float, inv, round |
| 13 | + |
| 14 | +export Dec, dec, @dec_str, number, normalize |
| 15 | + |
| 16 | +const DIGITS = 20 |
| 17 | + |
| 18 | +const DFP_MARKER = BigInt(0) |
| 19 | + |
| 20 | +struct Dec <: AbstractFloat |
| 21 | + sgn::Bool # true = negative |
| 22 | + pow::Int32 # power of 10 |
| 23 | + val::BigInt # value |
| 24 | +end |
| 25 | + |
| 26 | +Dec(val::Integer, scl) = Dec(val < 0, scl, abs(val)) |
| 27 | + |
| 28 | +const dectab = Vector{BigInt}(undef, 38) |
| 29 | + |
| 30 | +function __init__() |
| 31 | + for p = 1:38; dectab[p] = BigInt(10)^p; end |
| 32 | +end |
| 33 | + |
| 34 | +# Primitives to help make these functions more generic later on |
| 35 | + |
| 36 | +const IntDec = Union{Integer, Dec} |
| 37 | + |
| 38 | +_getsign(x::Dec) = x.sgn |
| 39 | +_getsign(x::Unsigned) = false |
| 40 | +_getsign(x::Integer) = x < 0 |
| 41 | + |
| 42 | +_getcoeff(x::Dec) = x.val |
| 43 | +_getcoeff(x::Integer) = BigInt(abs(x)) |
| 44 | + |
| 45 | +_getint(x::Dec) = x.val |
| 46 | +_getint(x::Integer) = abs(x) |
| 47 | + |
| 48 | +_getscale(x::Dec) = x.pow |
| 49 | +_getscale(x::Integer) = 0 |
| 50 | + |
| 51 | +_scale(x, d) = _getcoeff(x) * (d > length(dectab) ? BigInt(10)^d : dectab[d]) |
| 52 | + |
| 53 | +_eq(x::IntDec, y::IntDec) = _getint(x) == _getint(y) |
| 54 | + |
| 55 | +_lt(x::IntDec, y::IntDec) = _getint(x) < _getint(y) |
| 56 | + |
| 57 | +# Promotion rules |
| 58 | +Base.promote_rule(::Type{Dec}, ::Type{<:Real}) = Dec |
| 59 | + |
| 60 | +# override definitions in Base |
| 61 | +Base.promote_rule(::Type{BigFloat}, ::Type{Dec}) = Dec |
| 62 | +Base.promote_rule(::Type{BigInt}, ::Type{Dec}) = Dec |
| 63 | + |
| 64 | +# Addition |
| 65 | +function +(x::Dec, y::IntDec) |
| 66 | + # Quickly deal with zero case, so as not to worry about -0.0 cases |
| 67 | + iszero(x) && return y |
| 68 | + iszero(y) && return x |
| 69 | + # Make both the same scale |
| 70 | + xscl = _getscale(x) |
| 71 | + yscl = _getscale(y) |
| 72 | + dscl = xscl - yscl |
| 73 | + if dscl == 0 |
| 74 | + xval = _getcoeff(x) |
| 75 | + yval = _getcoeff(y) |
| 76 | + else |
| 77 | + xval, yval = dscl < 0 ? (_getint(x), _scale(y, -dscl)) : (_scale(x, dscl), _getint(y)) |
| 78 | + abs(xscl) < abs(yscl) && (xscl = yscl) |
| 79 | + end |
| 80 | + # Simple case where signs are the same |
| 81 | + (xsgn = _getsign(x)) == _getsign(y) && return Dec(xsgn, xscl, xval + yval) |
| 82 | + # Signs are different, we need to subtract, possibly change sign |
| 83 | + (diff = xval - yval) < 0 ? Dec(!xsgn, xscl, -diff) : Dec(xsgn, xscl, diff) |
| 84 | +end |
| 85 | ++(x::Integer, y::Dec) = y + x |
| 86 | + |
| 87 | +# Negation |
| 88 | +-(x::Dec) = Dec(!x.sgn, x.pow, x.val) |
| 89 | + |
| 90 | +# Subtraction |
| 91 | +-(x::Dec, y::IntDec) = x + -y |
| 92 | +-(x::Integer, y::Dec) = -x + y |
| 93 | + |
| 94 | +# Multiplication |
| 95 | +*(x::Dec, y::IntDec) = |
| 96 | + Dec(xor(_getsign(x), _getsign(y)), _getscale(x) + _getscale(y), _getint(x) * _getint(y)) |
| 97 | +*(x::Integer, y::Dec) = y * x |
| 98 | + |
| 99 | +# Inversion |
| 100 | +function Base.inv(x::Dec) |
| 101 | + str = string(x) |
| 102 | + if str[1] == '-' |
| 103 | + str = str[2:end] |
| 104 | + end |
| 105 | + b = ('.' in str) ? length(split(str, '.')[1]) : 0 |
| 106 | + c = round(BigInt(10)^(-x.pow + DIGITS) / x.val) |
| 107 | + q = (x.pow < 0) ? 1 - b - DIGITS : -b - DIGITS |
| 108 | + normalize(Dec(x.sgn, q, c)) |
| 109 | +end |
| 110 | + |
| 111 | +# Division |
| 112 | +/(x::Dec, y::Dec) = x * inv(y) |
| 113 | + |
| 114 | +# TODO exponentiation |
| 115 | + |
| 116 | +# Convert a string to a decimal, e.g. "0.01" -> Dec(0, -2, 1) |
| 117 | +function Base.parse(::Type{Dec}, str::AbstractString) |
| 118 | + 'e' in str && return parse(Dec, scinote(str)) |
| 119 | + c, q = parameters(('.' in str) ? split(str, '.') : str) |
| 120 | + Dec((str[1] == '-') ? 1 : 0, q, c) |
| 121 | +end |
| 122 | + |
| 123 | +dec(str::AbstractString) = parse(Dec, str) |
| 124 | + |
| 125 | +# Convert a number to a decimal |
| 126 | +Dec(num::Real) = parse(Dec, string(num)) |
| 127 | +Base.convert(::Type{Dec}, num::Real) = Dec(num::Real) |
| 128 | +dec(x::Real) = Dec(x) |
| 129 | +Dec(x::Dec) = x |
| 130 | + |
| 131 | +# Get Dec constructor parameters from string |
| 132 | +parameters(x::AbstractString) = (abs(parse(BigInt, x)), 0) |
| 133 | + |
| 134 | +# Get Dec constructor parameters from array |
| 135 | +function parameters(x::Array) |
| 136 | + c = parse(BigInt, join(x)) |
| 137 | + (abs(c), -length(x[2])) |
| 138 | +end |
| 139 | + |
| 140 | +const strzeros = repeat('0', 256) |
| 141 | + |
| 142 | +function outzeros(io::IO, cnt::Integer) |
| 143 | + for i = 1:(cnt>>8) |
| 144 | + print(io, strzeros) |
| 145 | + end |
| 146 | + print(io, strzeros[1:(cnt&255)]) |
| 147 | +end |
| 148 | + |
| 149 | +# Get decimal() argument from scientific notation |
| 150 | +function scinote(str::AbstractString) |
| 151 | + s = (str[1] == '-') ? "-" : "" |
| 152 | + n, expo = split(str, 'e') |
| 153 | + n = split(n, '.') |
| 154 | + if s == "-" |
| 155 | + n[1] = n[1][2:end] |
| 156 | + end |
| 157 | + if parse(Int64, expo) > 0 |
| 158 | + shift = parse(Int64, expo) - ((length(n) == 2) ? length(n[2]) : 0) |
| 159 | + s * join(n) * repeat("0", shift) |
| 160 | + else |
| 161 | + shift = -parse(Int64, expo) - ((length(n) == 2) ? length(n[1]) : length(n)) |
| 162 | + s * "0." * repeat("0", shift) * join(n) |
| 163 | + end |
| 164 | +end |
| 165 | + |
| 166 | +# Convert a decimal to a string |
| 167 | +function Base.print(io::IO, x::Dec) |
| 168 | + x.sgn && print(io, '-') |
| 169 | + if x.pow < 0 |
| 170 | + c = string(x.val) |
| 171 | + shift = x.pow + length(c) |
| 172 | + if shift > 0 |
| 173 | + print(io, c[1:shift], '.', c[(shift+1):end]) |
| 174 | + else |
| 175 | + print(io, "0.") |
| 176 | + outzeros(io, -shift) |
| 177 | + print(io, c) |
| 178 | + end |
| 179 | + else |
| 180 | + print(io, x.val) |
| 181 | + x.pow > 0 && outzeros(io, x.pow) |
| 182 | + end |
| 183 | +end |
| 184 | + |
| 185 | +# Zero/one value |
| 186 | +Base.zero(::Type{Dec}) = Dec(0, 0, 0) |
| 187 | +Base.one(::Type{Dec}) = Dec(0, 0, 1) |
| 188 | + |
| 189 | +# convert a decimal to any subtype of Real |
| 190 | +(::Type{T})(x::Dec) where {T<:Real} = parse(T, string(x)) |
| 191 | + |
| 192 | +# Convert a decimal to an integer if possible, a float if not |
| 193 | +function number(x::Dec) |
| 194 | + ix = (str = string(x) ; fx = parse(Float64, str); round(Int64, fx)) |
| 195 | + (ix == fx) ? ix : fx |
| 196 | +end |
| 197 | + |
| 198 | +# sign |
| 199 | +Base.signbit(x::Dec) = x.sgn |
| 200 | + |
| 201 | +# Equality |
| 202 | + |
| 203 | +Base.iszero(x::Dec) = iszero(x.val) |
| 204 | + |
| 205 | +# equals() now depends on == instead of the other way round. |
| 206 | + |
| 207 | +function ==(x::Dec, y::IntDec) |
| 208 | + # Check if both are zero, regardless of sign |
| 209 | + iszero(x) && return iszero(y) |
| 210 | + iszero(y) && return false |
| 211 | + # Make sure signs are the same |
| 212 | + _getsign(x) == _getsign(y) || return false |
| 213 | + # If scales are the same, don't bother to equalize the scales |
| 214 | + (d = _getscale(x) - _getscale(y)) == 0 && return _eq(x, y) |
| 215 | + # Find out how much we need to multiply by to equalize the scales |
| 216 | + # Note: further optimization would use tables to see if the size (in limbs, or bits) of the two operands |
| 217 | + # could possibly be ==, without even doing the (somewhat expensive) scaling operation. |
| 218 | + d < 0 ? _eq(x, _scale(y, -d)) : _eq(_scale(x, d), y) |
| 219 | +end |
| 220 | + |
| 221 | +==(x::Integer, y::Dec) = y == x |
| 222 | + |
| 223 | +function <(x::Dec, y::IntDec) |
| 224 | + # Check for both zeros, regardless of sign |
| 225 | + iszero(x) && return ifelse(iszero(y), false, !_getsign(y)) |
| 226 | + iszero(y) && return _getsign(x) |
| 227 | + # Check signs |
| 228 | + xsgn = _getsign(x) |
| 229 | + ysgn = _getsign(y) |
| 230 | + xsgn == ysgn || return xsgn |
| 231 | + # If scales are the same, don't bother to equalize the scales |
| 232 | + (dscl = _getscale(x) - _getscale(y)) == 0 && return xor(_lt(x, y), ysgn) |
| 233 | + # Find out how much we need to multiply by to equalize the scales |
| 234 | + # Note: further optimization would use tables to see if the size (in limbs, or bits) of the two operands |
| 235 | + # are such that one is definitely larger than the other, without even doing the (somewhat expensive) scaling operation. |
| 236 | + xor(dscl < 0 ? _lt(x, _scale(y, -dscl)) : _lt(_scale(x, dscl), y), ysgn) |
| 237 | +end |
| 238 | + |
| 239 | +function <(x::Integer, y::Dec) |
| 240 | + # Check for both zeros, regardless of sign |
| 241 | + iszero(x) && return ifelse(iszero(y), false, !_getsign(y)) |
| 242 | + iszero(y) && return _getsign(x) |
| 243 | + # Check signs |
| 244 | + xsgn = _getsign(x) |
| 245 | + ysgn = _getsign(y) |
| 246 | + xsgn == ysgn || return xsgn |
| 247 | + # If scales are the same, don't bother to equalize the scales |
| 248 | + xor((dscl = _getscale(y)) == 0 ? _lt(x, y) : |
| 249 | + (dscl < 0 ? _lt(_scale(x, -dscl), y) : _lt(x, _scale(y, dscl))), ysgn) |
| 250 | +end |
| 251 | + |
| 252 | +# Rounding |
| 253 | +function round(x::Dec; digits::Int=0, normal::Bool=false) |
| 254 | + shift = BigInt(digits) + x.pow |
| 255 | + if !(shift > BigInt(0) || shift < x.pow) |
| 256 | + c = Base.round(x.val / BigInt(10)^(-shift)) |
| 257 | + x = Dec(x.sgn, x.pow - shift, BigInt(c)) |
| 258 | + end |
| 259 | + normal ? x : normalize(x, rounded=true) |
| 260 | +end |
| 261 | + |
| 262 | +# Normalization: remove trailing zeros in coefficient |
| 263 | +function normalize(x::Dec; rounded::Bool=false) |
| 264 | + # Note: this is very inefficient |
| 265 | + # First, one can count the trailing zero bits, and that will give an indication |
| 266 | + # of the maximum 0 digits (because 10 is 5*2) |
| 267 | + p = 0 |
| 268 | + if x.val != 0 |
| 269 | + while x.val % BigInt(10)^(p+1) == 0 |
| 270 | + p += 1 |
| 271 | + end |
| 272 | + end |
| 273 | + c, r = divrem(x.val, BigInt(10)^p) |
| 274 | + q = (c == 0 && !x.sgn) ? 0 : x.pow + p |
| 275 | + v = Dec(x.sgn, q, abs(c)) |
| 276 | + rounded ? v : round(v, digits=DIGITS, normal=true) |
| 277 | +end |
| 278 | + |
| 279 | +end # Decs |
0 commit comments