Skip to content

Commit c5cc3f2

Browse files
authored
Merge pull request #33308 from JuliaLang/jq/compactfloat
Fix #33185 by rounding to 6 significant digits while printing float d…
2 parents 872d8ea + 1c280a7 commit c5cc3f2

File tree

5 files changed

+93
-24
lines changed

5 files changed

+93
-24
lines changed

base/ryu/Ryu.jl

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ neededdigits(::Type{Float32}) = 39 + 9 + 2
1010
neededdigits(::Type{Float16}) = 9 + 5 + 9
1111

1212
"""
13-
Ryu.writeshortest(x, plus=false, space=false, hash=true, precision=-1, expchar=UInt8('e'), padexp=false, decchar=UInt8('.'))
13+
Ryu.writeshortest(x, plus=false, space=false, hash=true, precision=-1, expchar=UInt8('e'), padexp=false, decchar=UInt8('.'), typed=false, compact=false)
1414
Ryu.writeshortest(buf::Vector{UInt8}, pos::Int, x, args...)
1515
1616
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:
2121
* `plus`: for positive `x`, prefix decimal string with a `'+'` character
2222
* `space`: for positive `x`, prefix decimal string with a `' '` character; overridden if `plus=true`
2323
* `hash`: whether the decimal point should be written, even if no additional digits are needed for precision
24-
* `precision`: minimum number of significant digits to be included in the decimal string; extra `'0'` characters will be added for padding if necessary
24+
* `precision`: minimum number of digits to be included in the decimal string; extra `'0'` characters will be added for padding if necessary
2525
* `expchar`: character to use exponent component in scientific notation
2626
* `padexp`: whether two digits should always be written, even for single-digit exponents (e.g. `e+1` becomes `e+01`)
2727
* `decchar`: decimal point character to be used
28+
* `typed`: whether additional type information should be printed for `Float16` / `Float32`
29+
* `compact`: output will be limited to 6 significant digits
2830
"""
2931
function writeshortest(x::T,
3032
plus::Bool=false,
@@ -33,17 +35,19 @@ function writeshortest(x::T,
3335
precision::Integer=-1,
3436
expchar::UInt8=UInt8('e'),
3537
padexp::Bool=false,
36-
decchar::UInt8=UInt8('.')) where {T <: Base.IEEEFloat}
38+
decchar::UInt8=UInt8('.'),
39+
typed::Bool=false,
40+
compact::Bool=false) where {T <: Base.IEEEFloat}
3741
buf = Base.StringVector(neededdigits(T))
38-
pos = writeshortest(buf, 1, x)
42+
pos = writeshortest(buf, 1, x, plus, space, hash, precision, expchar, padexp, decchar, typed, compact)
3943
return String(resize!(buf, pos - 1))
4044
end
4145

4246
"""
43-
Ryu.writefixed(x, plus=false, space=false, hash=true, precision=-1, decchar=UInt8('.'))
47+
Ryu.writefixed(x, precision, plus=false, space=false, hash=false, decchar=UInt8('.'), trimtrailingzeros=false)
4448
Ryu.writefixed(buf::Vector{UInt8}, pos::Int, x, args...)
4549
46-
Convert a float value `x` into a "fixed" size decimal string.
50+
Convert a float value `x` into a "fixed" size decimal string of the provided precision.
4751
This function allows achieving the `%f` printf format.
4852
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.
4953
@@ -53,15 +57,22 @@ Various options for the output format include:
5357
* `hash`: whether the decimal point should be written, even if no additional digits are needed for precision
5458
* `precision`: minimum number of significant digits to be included in the decimal string; extra `'0'` characters will be added for padding if necessary
5559
* `decchar`: decimal point character to be used
60+
* `trimtrailingzeros`: whether trailing zeros should be removed
5661
"""
57-
function writefixed(x::T, precision) where {T <: Base.IEEEFloat}
62+
function writefixed(x::T,
63+
precision::Integer,
64+
plus::Bool=false,
65+
space::Bool=false,
66+
hash::Bool=false,
67+
decchar::UInt8=UInt8('.'),
68+
trimtrailingzeros::Bool=false) where {T <: Base.IEEEFloat}
5869
buf = Base.StringVector(precision + neededdigits(T))
59-
pos = writefixed(buf, 1, x, false, false, false, precision)
70+
pos = writefixed(buf, 1, x, precision, plus, space, hash, decchar, trimtrailingzeros)
6071
return String(resize!(buf, pos - 1))
6172
end
6273

6374
"""
64-
Ryu.writeexp(x, plus=false, space=false, hash=true, precision=-1, expchar=UInt8('e'), decchar=UInt8('.'))
75+
Ryu.writeexp(x, precision, plus=false, space=false, hash=false, expchar=UInt8('e'), decchar=UInt8('.'), trimtrailingzeros=false)
6576
Ryu.writeexp(buf::Vector{UInt8}, pos::Int, x, args...)
6677
6778
Convert a float value `x` into a scientific notation decimal string.
@@ -75,32 +86,38 @@ Various options for the output format include:
7586
* `precision`: minimum number of significant digits to be included in the decimal string; extra `'0'` characters will be added for padding if necessary
7687
* `expchar`: character to use exponent component in scientific notation
7788
* `decchar`: decimal point character to be used
89+
* `trimtrailingzeros`: whether trailing zeros should be removed
7890
"""
79-
function writeexp(x::T, precision) where {T <: Base.IEEEFloat}
91+
function writeexp(x::T,
92+
precision::Integer,
93+
plus::Bool=false,
94+
space::Bool=false,
95+
hash::Bool=false,
96+
expchar::UInt8=UInt8('e'),
97+
decchar::UInt8=UInt8('.'),
98+
trimtrailingzeros::Bool=false) where {T <: Base.IEEEFloat}
8099
buf = Base.StringVector(precision + neededdigits(T))
81-
pos = writeexp(buf, 1, x, false, false, false, precision)
100+
pos = writeexp(buf, 1, x, precision, plus, space, hash, expchar, decchar, trimtrailingzeros)
82101
return String(resize!(buf, pos - 1))
83102
end
84103

85-
function Base.show(io::IO, x::T) where {T <: Base.IEEEFloat}
86-
if get(io, :compact, false)
87-
x = round(x, sigdigits=6)
88-
end
104+
function Base.show(io::IO, x::T, forceuntyped::Bool=false) where {T <: Base.IEEEFloat}
105+
compact = get(io, :compact, false)
89106
buf = Base.StringVector(neededdigits(T))
90-
typed = !get(io, :compact, false) && get(io, :typeinfo, Any) != typeof(x)
107+
typed = !forceuntyped && !compact && get(io, :typeinfo, Any) != typeof(x)
91108
pos = writeshortest(buf, 1, x, false, false, true, -1,
92-
x isa Float32 ? UInt8('f') : UInt8('e'), false, UInt8('.'), typed)
109+
x isa Float32 ? UInt8('f') : UInt8('e'), false, UInt8('.'), typed, compact)
93110
write(io, resize!(buf, pos - 1))
94111
return
95112
end
96113

97114
function Base.string(x::T) where {T <: Base.IEEEFloat}
98115
buf = Base.StringVector(neededdigits(T))
99116
pos = writeshortest(buf, 1, x, false, false, true, -1,
100-
x isa Float32 ? UInt8('f') : UInt8('e'), false, UInt8('.'), false)
117+
x isa Float32 ? UInt8('f') : UInt8('e'), false, UInt8('.'), false, false)
101118
return String(resize!(buf, pos - 1))
102119
end
103120

104-
Base.print(io::IO, x::Union{Float16, Float32}) = show(IOContext(io, :compact => true), x)
121+
Base.print(io::IO, x::Union{Float16, Float32}) = show(io, x, true)
105122

106123
end # module

base/ryu/exp.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
@inline function writeexp(buf, pos, v::T,
2-
plus=false, space=false, hash=false,
3-
precision=-1, expchar=UInt8('e'), decchar=UInt8('.'), trimtrailingzeros=false) where {T <: Base.IEEEFloat}
2+
precision=-1, plus=false, space=false, hash=false,
3+
expchar=UInt8('e'), decchar=UInt8('.'), trimtrailingzeros=false) where {T <: Base.IEEEFloat}
44
@assert 0 < pos <= length(buf)
55
x = Float64(v)
66
neg = signbit(x)

base/ryu/fixed.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
@inline function writefixed(buf, pos, v::T,
2-
plus=false, space=false, hash=false,
3-
precision=-1, decchar=UInt8('.'), trimtrailingzeros=false) where {T <: Base.IEEEFloat}
2+
precision=-1, plus=false, space=false, hash=false,
3+
decchar=UInt8('.'), trimtrailingzeros=false) where {T <: Base.IEEEFloat}
44
@assert 0 < pos <= length(buf)
55
x = Float64(v)
66
neg = signbit(x)

base/ryu/shortest.jl

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
@inline function writeshortest(buf::Vector{UInt8}, pos, x::T,
22
plus=false, space=false, hash=true,
3-
precision=-1, expchar=UInt8('e'), padexp=false, decchar=UInt8('.'), typed=false) where {T}
3+
precision=-1, expchar=UInt8('e'), padexp=false, decchar=UInt8('.'), typed=false, compact=false) where {T}
44
@assert 0 < pos <= length(buf)
55
neg = signbit(x)
66
# special cases
@@ -242,6 +242,36 @@
242242
pos += 1
243243
end
244244

245+
if compact && output > 999999
246+
lastdigit = output % 10
247+
while true
248+
output = div(output, 10)
249+
nexp += nexp != 0
250+
output > 999999 || break
251+
lastdigit = output % 10
252+
end
253+
if lastdigit == 9
254+
output += 1
255+
lastdigit = 0
256+
end
257+
if lastdigit == 9
258+
while true
259+
output = div(output, 10)
260+
nexp += nexp != 0
261+
output % 10 == 9 || break
262+
end
263+
output += 1
264+
elseif output % 10 == 0
265+
while true
266+
output = div(output, 10)
267+
nexp += nexp != 0
268+
output % 10 == 0 || break
269+
end
270+
else
271+
output += lastdigit > 4
272+
end
273+
end
274+
245275
olength = decimallength(output)
246276
exp_form = true
247277
pt = nexp + olength

test/ryu.jl

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -736,4 +736,26 @@ end
736736

737737
end # exp
738738

739+
@testset "compact" begin
740+
741+
writecompact(x) = Ryu.writeshortest(x, false, false, true, -1, UInt8('e'), false, UInt8('.'), false, true)
742+
743+
@test writecompact(0.49999999) == "0.5"
744+
@test writecompact(0.459999999) == "0.46"
745+
@test writecompact(0.20058603493384108) == "0.200586"
746+
@test writecompact(0.9999999) == "1.0"
747+
@test writecompact(0.1999999) == "0.2"
748+
@test writecompact(123.4567) == "123.457"
749+
@test writecompact(0.001234567) == "0.00123457"
750+
@test writecompact(0.1234567) == "0.123457"
751+
@test writecompact(1234567.0) == "123457.0"
752+
@test writecompact(12345678910.0) == "1.23457e10"
753+
@test writecompact(12345678.0) == "123457.0"
754+
@test writecompact(0.10000049) == "0.1"
755+
@test writecompact(22.89825) == "22.8983"
756+
@test writecompact(0.646690981531646) == "0.646691"
757+
@test writecompact(6.938893903907228e-17) == "6.93889e-17"
758+
759+
end
760+
739761
end # Ryu

0 commit comments

Comments
 (0)