|
1 | | -# Maps the format string (e.g. "%.5f") to the compiled formatter Function. |
2 | | -const LOCAL_FORMAT_CACHE = ThreadSafeDict{String, Function}() |
3 | | - |
4 | 1 | """ |
5 | | - format_with_precision(x; atol=eps(float(T)), mode=:auto, maxdigits=20) |
| 2 | + format_with_precision(x; atol=0.0, maxdigits=typemax(Int)) |
6 | 3 |
|
7 | | -Format number `x` as a string with absolute precision `atol`. |
8 | | -Mode `:auto` uses `%f` for `[1e-3, 1e6)`, else `%e`. Use `:f` or `:e` to force format. |
| 4 | +Format number `x` as a string with absolute precision `atol` and a maximum of |
| 5 | +`maxdigits` significant digits. |
| 6 | +Uses scientific notation for very small or large numbers. |
9 | 7 | """ |
10 | | -function format_with_precision(x::T; atol = eps(float(T)), mode::Symbol = :auto, maxdigits::Int = 20) where {T <: Real} |
| 8 | +function format_with_precision(x::T; atol = 0.0, maxdigits::Int = typemax(Int)) where {T <: Real} |
11 | 9 | iszero(x) && return "0.0" |
12 | | - absx = abs(x) |
13 | | - abs_atol = abs(atol) |
14 | 10 |
|
15 | | - use_e = mode == :e || (mode == :auto && (absx < 1.0e-3 || absx >= 1.0e6)) |
| 11 | + if iszero(atol) |
| 12 | + maxdigits == typemax(Int) && return string(x) |
| 13 | + return string(round(x, sigdigits = maxdigits)) |
| 14 | + end |
16 | 15 |
|
17 | | - if use_e |
18 | | - exp10 = floor(Int, log10(absx)) |
19 | | - log10_atol = log10(abs_atol) |
20 | | - p = ceil(Int, exp10 - log10_atol) |
21 | | - p = clamp(p, 0, maxdigits) |
| 16 | + mag_x = floor(Int, log10(abs(x))) |
| 17 | + mag_atol = floor(Int, log10(abs(atol))) |
22 | 18 |
|
23 | | - formatter = get_threadsafe_formatter("%.$(p)e") |
24 | | - return formatter(x) |
25 | | - else |
26 | | - p = ceil(Int, -log10(abs_atol)) |
27 | | - p = clamp(p, 0, maxdigits) |
| 19 | + required_sigdigits = max(1, mag_x - mag_atol + 1) |
| 20 | + s = min(required_sigdigits, maxdigits) |
28 | 21 |
|
29 | | - formatter = get_threadsafe_formatter("%.$(p)f") |
30 | | - return formatter(x) |
31 | | - end |
32 | | -end |
33 | | - |
34 | | -function get_threadsafe_formatter(fmtstr::String) |
35 | | - return get!(LOCAL_FORMAT_CACHE, fmtstr) do |
36 | | - Format.generate_formatter(fmtstr) |
37 | | - end |
| 22 | + rounded_x = round(x, sigdigits = s) |
| 23 | + return string(rounded_x) |
38 | 24 | end |
39 | 25 |
|
40 | 26 | read_number(V::Number) = V |
@@ -64,18 +50,18 @@ function _read_number_bracket(V::AbstractString) |
64 | 50 | return ComplexF64(real, imag) |
65 | 51 | end |
66 | 52 |
|
67 | | -function write_number(V::Number; atol::Real = eps(real(float(V)))) |
68 | | - return format_with_precision(V; atol = atol) |
| 53 | +function write_number(V::Number; kwargs...) |
| 54 | + return format_with_precision(V; kwargs...) |
69 | 55 | end |
70 | 56 |
|
71 | | -function write_number(V::Complex; atol::Real = eps(real(float(V)))) |
72 | | - return string("(", format_with_precision(real(V); atol = atol), ",", format_with_precision(imag(V); atol = atol), ")") |
| 57 | +function write_number(V::Complex; kwargs...) |
| 58 | + return string("(", format_with_precision(real(V); kwargs...), ",", format_with_precision(imag(V); kwargs...), ")") |
73 | 59 | end |
74 | 60 |
|
75 | | -function write_number_space(V::Number; atol::Real = eps(real(float(V)))) |
76 | | - return format_with_precision(V; atol = atol) |
| 61 | +function write_number_space(V::Number; kwargs...) |
| 62 | + return format_with_precision(V; kwargs...) |
77 | 63 | end |
78 | 64 |
|
79 | | -function write_number_space(V::Complex; atol::Real = eps(real(float(V)))) |
80 | | - return "$(format_with_precision(real(V); atol = atol)) $(format_with_precision(imag(V); atol = atol))" |
| 65 | +function write_number_space(V::Complex; kwargs...) |
| 66 | + return "$(format_with_precision(real(V); kwargs...)) $(format_with_precision(imag(V); kwargs...))" |
81 | 67 | end |
0 commit comments