speed up postdecimal digits#140
Conversation
|
Slower, but more accurate: function postdecimal_digits(x::T) where {T}
isinteger(x) && return 0
str = string(x)
i = findfirst('.', str)
return lastindex(str) - i
end@btime PlotUtils.postdecimal_digits(1.245)
72.293 ns (2 allocations: 432 bytes)
3 |
Worth adding a few tests covering up to Looks good otherwise. |
|
You should not use equality: PlotUtils.postdecimal_digits(1.245)
16 |
So we use the allocating version? |
|
I think reverting 9fc6af2 should be enough ? Not the string version, it's awful (relying on string formatting when working on floats). |
It's so far the only one that passes the tests apart from the original version |
|
What about this, which passes all current tests: function postdecimal_digits(x::T) where {T}
isinteger(x) && return 0
for i in 0:ceil(Int, log10(floatmax(T)))
x == floor(x; digits = i) && return i
end
return 0
end
@btime PlotUtils.postdecimal_digits(1.245)
35.118 ns (0 allocations: 0 bytes)
3
@btime PlotUtils.postdecimal_digits(floatmin(Float64))
16.020 μs (0 allocations: 0 bytes)
309
@btime PlotUtils.postdecimal_digits(floatmin(Float32))
1.389 μs (0 allocations: 0 bytes)
39 |
|
I am beginning to like this implementation function postdecimal_digits(x::T) where {T}
counter = 0
isinteger(x) && return 0
y = x
while !isinteger(y)
counter += 1
y = x * 10^counter
end
return counter
endhowever, it clips at 17 digits except for cases like |
|
Here is some some testing: using BenchmarkTools
using Distributions
using UnicodePlots
using StableRNGs
using Random
# old implementation
function postdecimal_digits(x::T) where {T}
for i ∈ floor(Int, log10(floatmin(T))):ceil(Int, log10(floatmax(T)))
x == floor(x; digits=i) && return max(0, i)
end
0
end
function postdecimal_digits_str(x)
isinteger(x) && return 0
isfinite(x) || return 0
str = string(x)
i = findfirst('.', str)
lastindex(str) - i
end
function PostDecimalDigits(x)
isinteger(x) && return 0
isfinite(x) || return 0
cnt = 0
y = x
while !isinteger(y) && cnt < 100
cnt += 1
y = x * 10^cnt
end
cnt
end
main() = begin
rng = StableRNG(1337)
Random.seed!(rng, 1337)
# func = (args...) -> nothing
# func = error
func = println
ndiff = 10
s = 4
for zctr in (true, false)
for i in -8:s:18
if zctr
μ = 0.
σ = 10.0^i
else
μ = 10.0^i
σ = 10.0^s
end
d = Normal(μ, σ)
errs = zeros(Int, ndiff) # error distribution
samples = 100_000
nok1 = nok2 = ok = 0
for (i, f) ∈ enumerate(rand(rng, d, samples))
old, new = postdecimal_digits(f), PostDecimalDigits(f)
if (Δ = abs(old - new)) == 0
ok += 1
else
msg = "$i) Δ=$Δ o=$old n=$new s=$(postdecimal_digits_str(f)) f=$(repr(f))"
# func(msg)
if Δ > ndiff
func(msg)
nok2 += 1
elseif Δ > 0
errs[Δ] += 1
nok1 += 1
end
end
end
println("$ok $nok1 $nok2")
lineplot(errs[1:end], title="err μ=$μ σ=$σ | ✓: $(round(100ok / samples; digits = 2))% | n°=$samples") |> display
end
end
f = rand(rng, Float64)
@show f
@btime postdecimal_digits($f)
@btime PostDecimalDigits($f)
return
end
main()The errors look quite large ... |
|
So if we take this one function postdecimal_digits(x::T) where {T}
isinteger(x) && return 0
for i in 0:ceil(Int, log10(floatmax(T)))
x == floor(x; digits = i) && return i
end
return 0
endwe get |
|
I guess anything smaller than machine epsilon will give you different results when working with different processors and math libraries (I don't want to go over https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html again ...). Maybe use |
I don't get this on linux: julia> versioninfo()
Julia Version 1.7.3
Commit 742b9abb4d (2022-05-06 12:58 UTC)
Platform Info:
OS: Linux (x86_64-pc-linux-gnu)
CPU: Intel(R) Core(TM) i7-8565U CPU @ 1.80GHz
WORD_SIZE: 64
LIBM: libopenlibm
LLVM: libLLVM-12.0.1 (ORCJIT, skylake)
Environment:
JULIA_NUM_THREADS = 8
julia> function postdecimal_digits(x::T) where {T}
isinteger(x) && return 0
for i in 0:ceil(Int, log10(floatmax(T)))
x == floor(x; digits=i) && return i
end
return 0
end
postdecimal_digits (generic function with 1 method)
julia> postdecimal_digits(2.5e-22)
23 |
On master:
This PR:
Not sure yet, that there aren't edge cases, where the results are different