Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ryu engineering2 #31

Merged
merged 5 commits into from
Feb 21, 2021
Merged

Conversation

isentropic
Copy link
Collaborator

@isentropic isentropic commented Feb 15, 2021

(@v1.5) pkg> test Showoff
    Testing Showoff
Status `/tmp/jl_ffG9Ue/Project.toml`
  [42e2da0e] Grisu v1.0.0
  [992d4aef] Showoff v0.3.2 `~/Software/Showoff`
  [ade2ca70] Dates
  [de0858da] Printf
  [8dfed614] Test
Status `/tmp/jl_ffG9Ue/Manifest.toml`
  [42e2da0e] Grisu v1.0.0
  [992d4aef] Showoff v0.3.2 `~/Software/Showoff`
  [2a0f44e3] Base64
  [ade2ca70] Dates
  [8ba89e20] Distributed
  [b77e0a4c] InteractiveUtils
  [56ddb016] Logging
  [d6f4376e] Markdown
  [de0858da] Printf
  [9a3f8284] Random
  [9e88b42a] Serialization
  [6462fe0b] Sockets
  [8dfed614] Test
  [4ec0a83e] Unicode
Test Summary: | Pass  Total
Internals     |    7      7
Test Summary: | Pass  Total
Formatting    |   16     16
Test Summary: | Pass  Total
Showoff       |   10     10
    Testing Showoff tests passed 

@timholy I'm creating a new PR due to some unresolved git issues I had with the previous commit. More details inside code review.

if isdefined(Base, :Grisu)
include("grisu.jl")
else
if isdefined(Base, :Ryu)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This way users from 1.5 can also benefit

src/ryu.jl Outdated
@@ -6,8 +6,11 @@ function plain_precision_heuristic(xs::AbstractArray{<:AbstractFloat})
ys = filter(isfinite, xs)
precision = 0
for y in ys
b, e10 = Ryu.reduce_shortest(convert(Float32, y))
precision = max(precision, -e10)
if y ≈ 0.0 # 0 digits for numbers close to 0
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem oftentimes arises due 0.0 present as usually 0.0 = 1e-55 (some weird power for floats). Then this would mean that if 0.0 is present, precision would go to 55. It is better to set that precision needed to display 0 should always be 0

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

julia> 1e-200  0.0
false

I think you'll be better off keeping a min/max precision and capping the ratio between them.

# 1.2334e5 = 123.334e3
# 1.2334-5 = 12.3334e-6

if positive
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic is commented above and should be easy enough to follow. Though this function is less efficient (due to int parsing), I believe it is worth it as it greatly simplifies the logic

@@ -1,6 +1,7 @@
using Showoff
using Test
using Dates
using Printf
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add printf as we should match the behavior to @sprintf as it was originally intended

@test Showoff.format_fixed_scientific(0.012345678, 4, false) == "1.2346×10⁻²"
@test Showoff.format_fixed_scientific(-10.0, 4, false) == "-1.0000×10¹"
@test Showoff.format_fixed_scientific(-10.0, 4, false) == "-1.0000×10¹"
@test Showoff.format_fixed_scientific(-10.0, 4, false)[1:end-5] == @sprintf("%0.4e", -10.0)[1:end-4]
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Compare first half (without exponent)

end

@testset "Showoff" begin
x = [1.12345, 4.5678]
@test showoff(x) == ["1.12345", "4.56780"]
@test showoff([0.0, 50000.0]) == ["0", "5×10⁴"]
@test showoff([0.0, 50000.0]) == ["0", "5.0×10⁴"]
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Slightly breaking, as now engineering notation introduces the trailing 0, however it is now consistent with scientific as it also introduces the zero

@isentropic
Copy link
Collaborator Author

Regarding your comments from #30 (review), for Plots, the performance of Showoff is completely insignificant, as showoff is only called twice (thrice) per plot. The time to first plot comes from the backends, not Plots, and especially not from showoff. Merging this would greatly improve the user experience as it the issue #18 has been a major annoyance for a long time

@isentropic
Copy link
Collaborator Author

The test were modified in order to match @sprintf but that is not a major breakage I assume.
Perhaps gadfly maintainers could input their opinion about the performance hit?
@dcjones @bjarthur

@isentropic
Copy link
Collaborator Author

Also I noticed that Makie too, uses this @SimonDanisch.
Perhaps you could also comment about the performance?

@isentropic isentropic closed this Feb 16, 2021
@isentropic isentropic reopened this Feb 16, 2021
@timholy
Copy link
Member

timholy commented Feb 16, 2021

Still not passing tests. Does it pass locally for you?

Copy link
Member

@timholy timholy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems close to mergeable, unless @SimonDanisch thinks the performance issues are serious. Certainly for Plots concerns about performance should not hold this back.

src/ryu.jl Outdated
@@ -6,8 +6,11 @@ function plain_precision_heuristic(xs::AbstractArray{<:AbstractFloat})
ys = filter(isfinite, xs)
precision = 0
for y in ys
b, e10 = Ryu.reduce_shortest(convert(Float32, y))
precision = max(precision, -e10)
if y ≈ 0.0 # 0 digits for numbers close to 0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

julia> 1e-200  0.0
false

I think you'll be better off keeping a min/max precision and capping the ratio between them.

src/ryu.jl Outdated Show resolved Hide resolved
src/ryu.jl Outdated Show resolved Hide resolved
test/runtests.jl Show resolved Hide resolved
@isentropic
Copy link
Collaborator Author

It's strange, I pass the tests locally

@timholy
Copy link
Member

timholy commented Feb 16, 2021

Are you testing with Pkg.test? Sometimes that's different than include("runtests.jl"). Although perhaps the main thing to check is that you've pushed all your changes?

isentropic and others added 2 commits February 17, 2021 13:07
Co-authored-by: Tim Holy <tim.holy@gmail.com>
src/ryu.jl Outdated
@@ -6,8 +6,11 @@ function plain_precision_heuristic(xs::AbstractArray{<:AbstractFloat})
ys = filter(isfinite, xs)
precision = 0
for y in ys
b, e10 = Ryu.reduce_shortest(convert(Float32, y))
precision = max(precision, -e10)
if isapprox(y, 0, atol=1e-16) # 0 has undefined rtol
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@timholy What about now?
The probelm with 0.0 is that it has undefined rtol and it is hard to judge if a number is close to zero
The choice behind 1e-16 is somewhat arbitrary

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My suggestion was to keep track of both the min and max of e10. Maybe something like this:

e10max = -(e10min = typemax(Int))
for y in ys
    _, e10 = Ryu.reduce_shortest(y)
    e10min = min(e10min, e10)
    e10max = max(e10max, e10)
end
precision = min(-e10min, -e10max+16)

That way if all the numbers are tiny, you won't truncate unnecessarily.

Not sure whether the old 0 was important for something, but this design would also allow you to incorporate that if necessary.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With your code

function plain_precision_heuristic(xs::AbstractArray{<:AbstractFloat})
    ys = filter(isfinite, xs)
    e10max = -(e10min = typemax(Int))
    for y in ys
        _, e10 = Ryu.reduce_shortest(y)
        e10min = min(e10min, e10)
        e10max = max(e10max, e10)
        @show e10min, e10max
    end
    return precision = min(-e10min, -e10max+16)

end

The showoff fails, and I think it is due to 0.0s:

julia> showoff([0.0, 50000.0])
(e10min, e10max) = (-325, -325)
(e10min, e10max) = (-325, 0)
2-element Array{String,1}:
 "0"
 "5.00000000000000000×10⁴"

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I still retain the zero number hack

function plain_precision_heuristic(xs::AbstractArray{<:AbstractFloat})
    ys = filter(isfinite, xs)
    e10max = -(e10min = typemax(Int))
    for y in ys
        if isapprox(y, 0, atol=1e-16)
            continue
        end
        _, e10 = Ryu.reduce_shortest(y)
        e10min = min(e10min, e10)
        e10max = max(e10max, e10)
        @show e10min, e10max
    end
    return precision = min(-e10min, -e10max+16)
end

and now the tests pass normally:

julia> showoff([0.0, 50000.0])
(e10min, e10max) = (0, 0)
2-element Array{String,1}:
 "0"
 "5.0×10⁴"

Copy link
Collaborator Author

@isentropic isentropic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More changes

@isentropic
Copy link
Collaborator Author

Regarding the tests, i pass them with both julia 1.5.3 and 1.4.2, of course the version of the julia needs to have Base.Ryu or else the tests would fail because of the fallback to Grisu.

~/Software/Showoff ryu-engineering2 ≡ 8s
❯ julia
               _
   _       _ _(_)_     |  Documentation: https://docs.julialang.org
  (_)     | (_) (_)    |
   _ _   _| |_  __ _   |  Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 1.5.3 (2020-11-09)
 _/ |\__'_|_|_|\__'_|  |  Official https://julialang.org/ release
|__/                   |

julia> include("test/runtests.jl")
Test Summary: | Pass  Total
Internals     |    7      7
Test Summary: | Pass  Total
Formatting    |   16     16
Test Summary: | Pass  Total
Showoff       |   10     10
Test.DefaultTestSet("Showoff", Any[], 10, false)

(@v1.5) pkg> test Showoff
    Testing Showoff
Status `/tmp/jl_2lmfLa/Project.toml`
  [42e2da0e] Grisu v1.0.0
  [992d4aef] Showoff v0.3.2 `~/Software/Showoff`
  [ade2ca70] Dates
  [de0858da] Printf
  [8dfed614] Test
Status `/tmp/jl_2lmfLa/Manifest.toml`
  [42e2da0e] Grisu v1.0.0
  [992d4aef] Showoff v0.3.2 `~/Software/Showoff`
  [2a0f44e3] Base64
  [ade2ca70] Dates
  [8ba89e20] Distributed
  [b77e0a4c] InteractiveUtils
  [56ddb016] Logging
  [d6f4376e] Markdown
  [de0858da] Printf
  [9a3f8284] Random
  [9e88b42a] Serialization
  [6462fe0b] Sockets
  [8dfed614] Test
  [4ec0a83e] Unicode
Test Summary: | Pass  Total
Internals     |    7      7
Test Summary: | Pass  Total
Formatting    |   16     16
Test Summary: | Pass  Total
Showoff       |   10     10
    Testing Showoff tests passed 

(@v1.5) pkg> 


@isentropic
Copy link
Collaborator Author

And thanks for looking into this issue @timholy and giving a code review, it is always helpful to have feedback 😄

Copy link
Member

@timholy timholy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just take a look at my suggestion for handling the precision in the case of small values, and then once you decide how you want to handle that we can merge this.

Regarding the tests, once this merges I'll check locally and probably update the tests to run on GitHub Actions before merging to master. So don't worry about it unless you hear otherwise from me later.

src/ryu.jl Outdated
@@ -6,8 +6,11 @@ function plain_precision_heuristic(xs::AbstractArray{<:AbstractFloat})
ys = filter(isfinite, xs)
precision = 0
for y in ys
b, e10 = Ryu.reduce_shortest(convert(Float32, y))
precision = max(precision, -e10)
if isapprox(y, 0, atol=1e-16) # 0 has undefined rtol
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My suggestion was to keep track of both the min and max of e10. Maybe something like this:

e10max = -(e10min = typemax(Int))
for y in ys
    _, e10 = Ryu.reduce_shortest(y)
    e10min = min(e10min, e10)
    e10max = max(e10max, e10)
end
precision = min(-e10min, -e10max+16)

That way if all the numbers are tiny, you won't truncate unnecessarily.

Not sure whether the old 0 was important for something, but this design would also allow you to incorporate that if necessary.

Copy link
Collaborator Author

@isentropic isentropic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handle 0s and min max exponents

src/ryu.jl Outdated
@@ -6,8 +6,11 @@ function plain_precision_heuristic(xs::AbstractArray{<:AbstractFloat})
ys = filter(isfinite, xs)
precision = 0
for y in ys
b, e10 = Ryu.reduce_shortest(convert(Float32, y))
precision = max(precision, -e10)
if isapprox(y, 0, atol=1e-16) # 0 has undefined rtol
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With your code

function plain_precision_heuristic(xs::AbstractArray{<:AbstractFloat})
    ys = filter(isfinite, xs)
    e10max = -(e10min = typemax(Int))
    for y in ys
        _, e10 = Ryu.reduce_shortest(y)
        e10min = min(e10min, e10)
        e10max = max(e10max, e10)
        @show e10min, e10max
    end
    return precision = min(-e10min, -e10max+16)

end

The showoff fails, and I think it is due to 0.0s:

julia> showoff([0.0, 50000.0])
(e10min, e10max) = (-325, -325)
(e10min, e10max) = (-325, 0)
2-element Array{String,1}:
 "0"
 "5.00000000000000000×10⁴"

src/ryu.jl Outdated
@@ -6,8 +6,11 @@ function plain_precision_heuristic(xs::AbstractArray{<:AbstractFloat})
ys = filter(isfinite, xs)
precision = 0
for y in ys
b, e10 = Ryu.reduce_shortest(convert(Float32, y))
precision = max(precision, -e10)
if isapprox(y, 0, atol=1e-16) # 0 has undefined rtol
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I still retain the zero number hack

function plain_precision_heuristic(xs::AbstractArray{<:AbstractFloat})
    ys = filter(isfinite, xs)
    e10max = -(e10min = typemax(Int))
    for y in ys
        if isapprox(y, 0, atol=1e-16)
            continue
        end
        _, e10 = Ryu.reduce_shortest(y)
        e10min = min(e10min, e10)
        e10max = max(e10max, e10)
        @show e10min, e10max
    end
    return precision = min(-e10min, -e10max+16)
end

and now the tests pass normally:

julia> showoff([0.0, 50000.0])
(e10min, e10max) = (0, 0)
2-element Array{String,1}:
 "0"
 "5.0×10⁴"

@timholy
Copy link
Member

timholy commented Feb 21, 2021

That's a pretty ugly hack but let's go with it (I really don't use this package so...). I'll merge this into my branch and then see whether I can figure out the test failures. Thanks!

@timholy timholy merged commit 6d7a7e5 into JuliaGraphics:teh/grisu Feb 21, 2021
@timholy
Copy link
Member

timholy commented Feb 21, 2021

OK, so the problem is that the tests were failing on older versions of Julia, they were passing on 1.6 (which I presume is what you're using). Some of the things changed behaviors that look like bugs of the old spec; however, you also have some new bugs:

julia> Showoff.format_fixed_scientific(-10.0, 4, true)
"-1.0000×10⁰"

which we need to fix before we can merge this to master. (It should be -10.0000×10⁰.)

Finally, some changes you made to @testset "Showoff" only pertain to the Ryu code, not the Grisu code.

I've put more time into this than it warrants so I'm going to have to rely on you to make these fixes, then I'll wrap everything up and merge.

timholy added a commit that referenced this pull request Feb 23, 2021
Co-authored-by: Tim Holy <tim.holy@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants