diff --git a/base/ryu/Ryu.jl b/base/ryu/Ryu.jl index 5d177cfd3cda9..ef2c105509726 100644 --- a/base/ryu/Ryu.jl +++ b/base/ryu/Ryu.jl @@ -10,7 +10,7 @@ neededdigits(::Type{Float32}) = 39 + 9 + 2 neededdigits(::Type{Float16}) = 9 + 5 + 9 """ - Ryu.writeshortest(x, plus=false, space=false, hash=true, precision=-1, expchar=UInt8('e'), padexp=false, decchar=UInt8('.')) + Ryu.writeshortest(x, plus=false, space=false, hash=true, precision=-1, expchar=UInt8('e'), padexp=false, decchar=UInt8('.'), typed=false, compact=false) Ryu.writeshortest(buf::Vector{UInt8}, pos::Int, x, args...) Convert a float value `x` into its "shortest" decimal string, which can be parsed back to the same value. @@ -21,10 +21,12 @@ Various options for the output format include: * `plus`: for positive `x`, prefix decimal string with a `'+'` character * `space`: for positive `x`, prefix decimal string with a `' '` character; overridden if `plus=true` * `hash`: whether the decimal point should be written, even if no additional digits are needed for precision - * `precision`: minimum number of significant digits to be included in the decimal string; extra `'0'` characters will be added for padding if necessary + * `precision`: minimum number of digits to be included in the decimal string; extra `'0'` characters will be added for padding if necessary * `expchar`: character to use exponent component in scientific notation * `padexp`: whether two digits should always be written, even for single-digit exponents (e.g. `e+1` becomes `e+01`) * `decchar`: decimal point character to be used + * `typed`: whether additional type information should be printed for `Float16` / `Float32` + * `compact`: output will be limited to 6 significant digits """ function writeshortest(x::T, plus::Bool=false, @@ -33,17 +35,19 @@ function writeshortest(x::T, precision::Integer=-1, expchar::UInt8=UInt8('e'), padexp::Bool=false, - decchar::UInt8=UInt8('.')) where {T <: Base.IEEEFloat} + decchar::UInt8=UInt8('.'), + typed::Bool=false, + compact::Bool=false) where {T <: Base.IEEEFloat} buf = Base.StringVector(neededdigits(T)) - pos = writeshortest(buf, 1, x) + pos = writeshortest(buf, 1, x, plus, space, hash, precision, expchar, padexp, decchar, typed, compact) return String(resize!(buf, pos - 1)) end """ - Ryu.writefixed(x, plus=false, space=false, hash=true, precision=-1, decchar=UInt8('.')) + Ryu.writefixed(x, precision, plus=false, space=false, hash=false, decchar=UInt8('.'), trimtrailingzeros=false) Ryu.writefixed(buf::Vector{UInt8}, pos::Int, x, args...) -Convert a float value `x` into a "fixed" size decimal string. +Convert a float value `x` into a "fixed" size decimal string of the provided precision. This function allows achieving the `%f` printf format. Note the 2nd method allows passing in a byte buffer and position directly; callers must ensure the buffer has sufficient room to hold the entire decimal string. @@ -53,15 +57,22 @@ Various options for the output format include: * `hash`: whether the decimal point should be written, even if no additional digits are needed for precision * `precision`: minimum number of significant digits to be included in the decimal string; extra `'0'` characters will be added for padding if necessary * `decchar`: decimal point character to be used + * `trimtrailingzeros`: whether trailing zeros should be removed """ -function writefixed(x::T, precision) where {T <: Base.IEEEFloat} +function writefixed(x::T, + precision::Integer, + plus::Bool=false, + space::Bool=false, + hash::Bool=false, + decchar::UInt8=UInt8('.'), + trimtrailingzeros::Bool=false) where {T <: Base.IEEEFloat} buf = Base.StringVector(precision + neededdigits(T)) - pos = writefixed(buf, 1, x, false, false, false, precision) + pos = writefixed(buf, 1, x, precision, plus, space, hash, decchar, trimtrailingzeros) return String(resize!(buf, pos - 1)) end """ - Ryu.writeexp(x, plus=false, space=false, hash=true, precision=-1, expchar=UInt8('e'), decchar=UInt8('.')) + Ryu.writeexp(x, precision, plus=false, space=false, hash=false, expchar=UInt8('e'), decchar=UInt8('.'), trimtrailingzeros=false) Ryu.writeexp(buf::Vector{UInt8}, pos::Int, x, args...) Convert a float value `x` into a scientific notation decimal string. @@ -75,21 +86,27 @@ Various options for the output format include: * `precision`: minimum number of significant digits to be included in the decimal string; extra `'0'` characters will be added for padding if necessary * `expchar`: character to use exponent component in scientific notation * `decchar`: decimal point character to be used + * `trimtrailingzeros`: whether trailing zeros should be removed """ -function writeexp(x::T, precision) where {T <: Base.IEEEFloat} +function writeexp(x::T, + precision::Integer, + plus::Bool=false, + space::Bool=false, + hash::Bool=false, + expchar::UInt8=UInt8('e'), + decchar::UInt8=UInt8('.'), + trimtrailingzeros::Bool=false) where {T <: Base.IEEEFloat} buf = Base.StringVector(precision + neededdigits(T)) - pos = writeexp(buf, 1, x, false, false, false, precision) + pos = writeexp(buf, 1, x, precision, plus, space, hash, expchar, decchar, trimtrailingzeros) return String(resize!(buf, pos - 1)) end -function Base.show(io::IO, x::T) where {T <: Base.IEEEFloat} - if get(io, :compact, false) - x = round(x, sigdigits=6) - end +function Base.show(io::IO, x::T, forceuntyped::Bool=false) where {T <: Base.IEEEFloat} + compact = get(io, :compact, false) buf = Base.StringVector(neededdigits(T)) - typed = !get(io, :compact, false) && get(io, :typeinfo, Any) != typeof(x) + typed = !forceuntyped && !compact && get(io, :typeinfo, Any) != typeof(x) pos = writeshortest(buf, 1, x, false, false, true, -1, - x isa Float32 ? UInt8('f') : UInt8('e'), false, UInt8('.'), typed) + x isa Float32 ? UInt8('f') : UInt8('e'), false, UInt8('.'), typed, compact) write(io, resize!(buf, pos - 1)) return end @@ -97,10 +114,10 @@ end function Base.string(x::T) where {T <: Base.IEEEFloat} buf = Base.StringVector(neededdigits(T)) pos = writeshortest(buf, 1, x, false, false, true, -1, - x isa Float32 ? UInt8('f') : UInt8('e'), false, UInt8('.'), false) + x isa Float32 ? UInt8('f') : UInt8('e'), false, UInt8('.'), false, false) return String(resize!(buf, pos - 1)) end -Base.print(io::IO, x::Union{Float16, Float32}) = show(IOContext(io, :compact => true), x) +Base.print(io::IO, x::Union{Float16, Float32}) = show(io, x, true) end # module \ No newline at end of file diff --git a/base/ryu/exp.jl b/base/ryu/exp.jl index 977969b99bd97..a083296c91d2b 100644 --- a/base/ryu/exp.jl +++ b/base/ryu/exp.jl @@ -1,6 +1,6 @@ @inline function writeexp(buf, pos, v::T, - plus=false, space=false, hash=false, - precision=-1, expchar=UInt8('e'), decchar=UInt8('.'), trimtrailingzeros=false) where {T <: Base.IEEEFloat} + precision=-1, plus=false, space=false, hash=false, + expchar=UInt8('e'), decchar=UInt8('.'), trimtrailingzeros=false) where {T <: Base.IEEEFloat} @assert 0 < pos <= length(buf) x = Float64(v) neg = signbit(x) diff --git a/base/ryu/fixed.jl b/base/ryu/fixed.jl index 06512982b14ec..af2a2abcf917f 100644 --- a/base/ryu/fixed.jl +++ b/base/ryu/fixed.jl @@ -1,6 +1,6 @@ @inline function writefixed(buf, pos, v::T, - plus=false, space=false, hash=false, - precision=-1, decchar=UInt8('.'), trimtrailingzeros=false) where {T <: Base.IEEEFloat} + precision=-1, plus=false, space=false, hash=false, + decchar=UInt8('.'), trimtrailingzeros=false) where {T <: Base.IEEEFloat} @assert 0 < pos <= length(buf) x = Float64(v) neg = signbit(x) diff --git a/base/ryu/shortest.jl b/base/ryu/shortest.jl index 28f17d1479392..597a3c160eed2 100644 --- a/base/ryu/shortest.jl +++ b/base/ryu/shortest.jl @@ -1,6 +1,6 @@ @inline function writeshortest(buf::Vector{UInt8}, pos, x::T, plus=false, space=false, hash=true, - precision=-1, expchar=UInt8('e'), padexp=false, decchar=UInt8('.'), typed=false) where {T} + precision=-1, expchar=UInt8('e'), padexp=false, decchar=UInt8('.'), typed=false, compact=false) where {T} @assert 0 < pos <= length(buf) neg = signbit(x) # special cases @@ -242,6 +242,36 @@ pos += 1 end + if compact && output > 999999 + lastdigit = output % 10 + while true + output = div(output, 10) + nexp += nexp != 0 + output > 999999 || break + lastdigit = output % 10 + end + if lastdigit == 9 + output += 1 + lastdigit = 0 + end + if lastdigit == 9 + while true + output = div(output, 10) + nexp += nexp != 0 + output % 10 == 9 || break + end + output += 1 + elseif output % 10 == 0 + while true + output = div(output, 10) + nexp += nexp != 0 + output % 10 == 0 || break + end + else + output += lastdigit > 4 + end + end + olength = decimallength(output) exp_form = true pt = nexp + olength diff --git a/test/ryu.jl b/test/ryu.jl index 51dcb4a456ac6..9a7ed91155676 100644 --- a/test/ryu.jl +++ b/test/ryu.jl @@ -736,4 +736,26 @@ end end # exp +@testset "compact" begin + + writecompact(x) = Ryu.writeshortest(x, false, false, true, -1, UInt8('e'), false, UInt8('.'), false, true) + + @test writecompact(0.49999999) == "0.5" + @test writecompact(0.459999999) == "0.46" + @test writecompact(0.20058603493384108) == "0.200586" + @test writecompact(0.9999999) == "1.0" + @test writecompact(0.1999999) == "0.2" + @test writecompact(123.4567) == "123.457" + @test writecompact(0.001234567) == "0.00123457" + @test writecompact(0.1234567) == "0.123457" + @test writecompact(1234567.0) == "123457.0" + @test writecompact(12345678910.0) == "1.23457e10" + @test writecompact(12345678.0) == "123457.0" + @test writecompact(0.10000049) == "0.1" + @test writecompact(22.89825) == "22.8983" + @test writecompact(0.646690981531646) == "0.646691" + @test writecompact(6.938893903907228e-17) == "6.93889e-17" + +end + end # Ryu