Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
fail-fast: false
matrix:
version:
- "1.6" # oldest supported version
- "1.7" # oldest supported version
- "1" # Latest Release
os:
- ubuntu-latest
Expand Down
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b"
[compat]
DecFP_jll = "2.0.3"
SpecialFunctions = "0.8, 0.9, 0.10, 0.11, 1, 2"
julia = "1.6"
julia = "1.7"
Printf = "<0.0.1, 1"
Random = "<0.0.1, 1"

Expand Down
357 changes: 286 additions & 71 deletions src/DecFP.jl
Original file line number Diff line number Diff line change
Expand Up @@ -399,77 +399,6 @@
return
end

function Printf.Printf.fix_dec(x::$BID, n::Int, digits)
if n > length(digits) - 1
n = length(digits) - 1
end
rounded = round(ldexp10(x, n), RoundNearestTiesAway)
if rounded == 0
digits[1] = UInt8('0')
return Int32(1), Int32(1), signbit(x)
end
tostring(rounded)
buffer = _stringbuffer()
trailing_zeros = 0
i = 2
while buffer[i] != UInt8('E')
digits[i - 1] = buffer[i]
if buffer[i] == UInt8('0')
trailing_zeros += 1
else
trailing_zeros = 0
end
i += 1
end
ndigits = i - 2
len = ndigits - trailing_zeros
i += 1
if buffer[i] == UInt8('+')
expsign = +1
elseif buffer[i] == UInt8('-')
expsign = -1
end
exponent = 0
i += 1
while buffer[i] != 0x00
exponent = exponent * 10 + buffer[i] - UInt8('0')
i += 1
end
exponent *= expsign
pt = ndigits + exponent - n
neg = signbit(x)
return Int32(len), Int32(pt), neg
end

function Printf.Printf.ini_dec(x::$BID, n::Int, digits)
if n > length(digits) - 1
n = length(digits) - 1
end
if x == 0
for i = 1:n
digits[i] = UInt8('0')
end
return Int32(1), Int32(1), signbit(x)
end
normalized_exponent = exponent10(x)
rounded = round(ldexp10(x, n - 1 - normalized_exponent), RoundNearestTiesAway)
rounded_exponent = exponent10(rounded)
tostring(rounded)
buffer = _stringbuffer()
i = 2
while buffer[i] != UInt8('E')
digits[i - 1] = buffer[i]
i += 1
end
while i <= n + 1
digits[i - 1] = UInt8('0')
i += 1
end
pt = normalized_exponent + rounded_exponent - n + 2
neg = signbit(x)
return Int32(n), Int32(pt), neg
end

function sigexp(x::$BID)
isnan(x) && throw(DomainError(x, "sigexp(x) is not defined for NaN."))
isinf(x) && throw(DomainError(x, "sigexp(x) is only defined for finite x."))
Expand Down Expand Up @@ -791,6 +720,292 @@
Base.widen(::Type{Dec32}) = Dec64
Base.widen(::Type{Dec64}) = Dec128

# Printf

# max exponent: `ceil(Int, log10(floatmax(T)))`
max_integer_part_width(::Type{Dec32}) = 97
max_integer_part_width(::Type{Dec64}) = 385
max_integer_part_width(::Type{Dec128}) = 6145

Printf.plength(f::Printf.Spec{S}, x::T) where {S<:Printf.Floats, T<:DecimalFloatingPoint} = max(f.width, max_integer_part_width(T) + f.precision + 2)

Printf.tofloat(x::DecimalFloatingPoint) = x

@inline function Printf.fmt(buf, pos, x::DecimalFloatingPoint, spec::Printf.Spec{T}) where {T<:Printf.Floats}
leftalign, plus, space, zero, hash, width, prec =
spec.leftalign, spec.plus, spec.space, spec.zero, spec.hash, spec.width, spec.precision
if T == Val{'e'} || T == Val{'E'}
newpos = Base.Ryu.writeexp(buf, pos, x, prec, plus, space, hash, Printf.char(T), UInt8('.'))
elseif T == Val{'f'} || T == Val{'F'}
newpos = Base.Ryu.writefixed(buf, pos, x, prec, plus, space, hash, UInt8('.'))
elseif T == Val{'g'} || T == Val{'G'}
if isinf(x) || isnan(x)
newpos = Base.Ryu.writeshortest(buf, pos, x, plus, space)
else
# C11-compliant general format
prec = prec == 0 ? 1 : prec
_, sig, dexp = sigexp(x)
numdig = ndigits(sig)
exp = dexp + numdig - 1
if -4 ≤ exp < prec
newpos = Base.Ryu.writefixed(buf, pos, x, prec - (exp + 1), plus, space, hash, UInt8('.'), !hash)
else
newpos = Base.Ryu.writeexp(buf, pos, x, prec - 1, plus, space, hash, T == Val{'g'} ? UInt8('e') : UInt8('E'), UInt8('.'), !hash)
end
end
elseif T == Val{'a'} || T == Val{'A'}
throw(ArgumentError("%a format specifier is not implemented for DecimalFloatingPoint"))
end
if newpos - pos < width
# need to pad
if leftalign
# easy case, just pad spaces after number
for _ = 1:(width - (newpos - pos))
buf[newpos] = UInt8(' ')
newpos += 1
end
else
# right aligned
n = width - (newpos - pos)
if zero && isfinite(x)
ex = (x < 0 || (plus | space)) + (T <: Union{Val{'a'}, Val{'A'}} ? 2 : 0)
so = pos + ex
len = (newpos - pos) - ex
copyto!(buf, so + n, buf, so, len)
for i = so:(so + n - 1)
buf[i] = UInt8('0')
end
newpos += n
else
copyto!(buf, pos + n, buf, pos, newpos - pos)
for i = pos:(pos + n - 1)
buf[i] = UInt8(' ')
end
newpos += n
end
end
end
return newpos
end

function Base.Ryu.writefixed(buf, pos, x::T, precision=-1, plus=false, space=false, hash=false, decchar=UInt8('.'), trimtrailingzeros=false) where {T<:DecimalFloatingPoint}
pos = Base.Ryu.append_sign(x, plus, space, buf, pos)

# special cases
if iszero(x)
buf[pos] = UInt8('0')
pos += 1
if precision > 0 && !trimtrailingzeros
buf[pos] = decchar
pos += 1
for _ = 1:precision
buf[pos] = UInt8('0')
pos += 1
end
elseif hash
buf[pos] = decchar
pos += 1
end
return pos
elseif isnan(x)
buf[pos] = UInt8('N')
buf[pos + 1] = UInt8('a')
buf[pos + 2] = UInt8('N')
return pos + 3
elseif !isfinite(x)
buf[pos] = UInt8('I')
buf[pos + 1] = UInt8('n')
buf[pos + 2] = UInt8('f')
return pos + 3
end

_, sig, exp = sigexp(x)
numdig = ndigits(sig)
digits = codeunits(string(sig))
i = numdig
while digits[i] == UInt8('0')
sig ÷= 10
exp += 1
numdig -= 1
i -= 1
end
if trimtrailingzeros && exp <= 0 && -exp < precision
precision = -exp
end
reqdig = numdig + exp + precision
if reqdig < 0
buf[pos] = UInt8('0')
pos += 1
if precision > 0 && !trimtrailingzeros
buf[pos] = decchar
pos += 1
for _ = 1:precision
buf[pos] = UInt8('0')
pos += 1
end
elseif hash
buf[pos] = decchar
pos += 1
end
return pos
end
if reqdig < numdig
diff = numdig - reqdig
if diff == 1
denominator = oftype(sig, 10)
elseif diff == 2
denominator = oftype(sig, 100)
elseif diff == 3
denominator = oftype(sig, 1000)
else
denominator = oftype(sig, 10)^Int32(diff)
end
sig = div(sig, denominator, RoundNearest)
exp += diff
end
digits = codeunits(string(sig))
if exp <= -length(digits)
buf[pos] = UInt8('0')
buf[pos + 1] = decchar
pos += 2
for i in 1:-(exp + length(digits))
buf[pos] = UInt8('0')
pos += 1
end
end
decindex = length(digits) + exp
for i in 1:length(digits)
buf[pos] = digits[i]
pos += 1
if i == decindex && (precision > 0 || hash)
buf[pos] = decchar
pos += 1
end
end
for i in numdig+1:reqdig
i > decindex && trimtrailingzeros && break
buf[pos] = UInt8('0')
pos += 1
if i == decindex && (hash || i != reqdig)
trimtrailingzeros && break
buf[pos] = decchar
pos += 1
end
end
return pos
end

function Base.Ryu.writeexp(buf, pos, x::T, precision=-1, plus=false, space=false, hash=false, expchar=UInt8('e'), decchar=UInt8('.'), trimtrailingzeros=false) where {T<:DecimalFloatingPoint}
pos = Base.Ryu.append_sign(x, plus, space, buf, pos)

# special cases
if iszero(x)
@inbounds buf[pos] = UInt8('0')
pos += 1
if precision > 0 && !trimtrailingzeros
@inbounds buf[pos] = decchar
pos += 1
for _ = 1:precision
@inbounds buf[pos] = UInt8('0')
pos += 1
end

Check warning on line 911 in src/DecFP.jl

View check run for this annotation

Codecov / codecov/patch

src/DecFP.jl#L906-L911

Added lines #L906 - L911 were not covered by tests
elseif hash
@inbounds buf[pos] = decchar
pos += 1
end
@inbounds buf[pos] = expchar
@inbounds buf[pos + 1] = UInt8('+')
@inbounds buf[pos + 2] = UInt8('0')
@inbounds buf[pos + 3] = UInt8('0')
return pos + 4
elseif isnan(x)
@inbounds buf[pos] = UInt8('N')
@inbounds buf[pos + 1] = UInt8('a')
@inbounds buf[pos + 2] = UInt8('N')
return pos + 3
elseif !isfinite(x)
@inbounds buf[pos] = UInt8('I')
@inbounds buf[pos + 1] = UInt8('n')
@inbounds buf[pos + 2] = UInt8('f')
return pos + 3
end

_, sig, exp = sigexp(x)
numdig = ndigits(sig)
if numdig > precision + 1
diff = numdig - precision - 1
denominator = oftype(sig, 10)^Int32(diff)
sig = div(sig, denominator, RoundNearest)
exp += diff
end
numdig = ndigits(sig)
if numdig > precision + 1
# leading digit 9 rounded up to 10
sig ÷= 10
exp += 1
end
digits = codeunits(string(sig))
numdig = length(digits)
if trimtrailingzeros
i = numdig
while digits[i] == UInt8('0')
exp += 1
numdig -= 1
i -= 1
end
end
pexp = exp + numdig - 1
buf[pos] = digits[1]
pos += 1
if numdig > 1 || (precision > 0 && !trimtrailingzeros) || hash
buf[pos] = decchar
pos += 1
end
for i in 2:numdig
buf[pos] = digits[i]
pos += 1
end
if !trimtrailingzeros
for _ in numdig:precision
buf[pos] = UInt8('0')
pos += 1
end
end
buf[pos] = expchar
pos += 1
if pexp < 0
@inbounds buf[pos] = UInt8('-')
pos += 1
pexp = -pexp
else
@inbounds buf[pos] = UInt8('+')
pos += 1
end
if pexp >= 1000
c = (pexp % 100) % UInt8
@inbounds d100 = Base._dec_d100[div(pexp, 100) + 1]
@inbounds buf[pos] = d100 % UInt8
@inbounds buf[pos + 1] = (d100 >> 0x8) % UInt8
@inbounds d100 = Base._dec_d100[c + 1]
@inbounds buf[pos + 2] = d100 % UInt8
@inbounds buf[pos + 3] = (d100 >> 0x8) % UInt8
pos += 4
elseif pexp >= 100
c = (pexp % 10) % UInt8
@inbounds d100 = Base._dec_d100[div(pexp, 10) + 1]
@inbounds buf[pos] = d100 % UInt8
@inbounds buf[pos + 1] = (d100 >> 0x8) % UInt8
@inbounds buf[pos + 2] = UInt8('0') + c
pos += 3
else
@inbounds d100 = Base._dec_d100[pexp + 1]
@inbounds buf[pos] = d100 % UInt8
@inbounds buf[pos + 1] = (d100 >> 0x8) % UInt8
pos += 2
end
return pos
end

macro d_str(s, flags...) parse(Dec64, s) end
macro d32_str(s, flags...) parse(Dec32, s) end
macro d64_str(s, flags...) parse(Dec64, s) end
Expand Down
Loading
Loading