chandra
December 12, 2023, 2:37pm
1
I wish to restrict the number of significant digits displayed on the REPL according to a context-dependent format like format
in MATLAB or Octave.
I have looked through the documentation, but have not seen what I am looking for.
Is this function available as standard or via a package?
1 Like
Check this post for a Matlab-like solution.
1 Like
chandra
December 12, 2023, 5:42pm
3
I have written a function to invoke within the REPL, called say, display_digits
like so:
function display_digits(n)
using Printf
Base.show(io::IO, f::Float64) = @printf(io, "%.nf", f)
end
I have these questions:
Where should this function file reside so that Julia sees it automatically?
How should I invoke this function?
How might the function be improved? [AFAICT it does not work at present].
Thanks.
To use a variable in the Printf
format string, see for example this post .
You could define your function in the startup.jl
file.
But why doesn’t the language itself provide a solution for this? Users interest is not lacking.
And what do these errors mean?
julia> function format(n) Base.show(io::IO, f::Float64) = Printf.format(io, "%.nf", f) end
ERROR: syntax: Global method definition around REPL[9]:1 needs to be placed at the top level, or use "eval".
1 Like
You might also have a look at 18228
You can’t overload a global function (e.g. Base.show
) inside another function, without using eval
.
Note that overloading Base.show
like this is generally a bad idea — it will effect everything , not just the REPL. (e.g. code doing I/O to files, over the network, … anything directly or indirectly using show
.) Type piracy is not composable.
This is more of a REPL issue than a language issue, I would say.
Implementing something both flexible and composable requires a bit of care, but is do-able by someone who understands Julia’s I/O and IOContext
architecture, I think. It’s not a 15-minute job, though, and someone has to volunteer do the work.
4 Likes
jar1
December 12, 2023, 9:05pm
9
@MA_Laforge has discussed these issues in NumericIO.jl.
stevengj:
Implementing something both flexible and composable requires a bit of care, but is do-able by someone who understands Julia’s I/O and IOContext
architecture, I think. It’s not a 15-minute job, though, and someone has to volunteer do the work.
I volunteered. A possible PR to provide a simple interface for changing the display precision is:
JuliaLang:master
← JuliaLang:sgj/displaydigits
opened 03:22AM - 15 Dec 23 UTC
Closes #6493: gives a way to control the REPL display precision (the number of s… ignificant digits displayed for floating-point values).
This is a very common request from users. e.g. on discourse see [here](https://discourse.julialang.org/t/how-do-i-restrict-the-number-of-digits-in-the-repl-display/107501) and [here](https://discourse.julialang.org/t/scientific-notation-in-repl/79841) and [here](https://discourse.julialang.org/t/how-to-print-a-number-to-x-digits/50526) and [here](https://discourse.julialang.org/t/how-to-display-float64-with-more-digits/5628) and [here](https://discourse.julialang.org/t/compact-float-printing-in-the-repl/4561). A lot of the suggested solutions involve redefining `show(::IO, ::Float64)`, which is type piracy and non-composable.
I wanted a solution which:
* Only affects `display` (what the REPL uses to show results) and not I/O to any other stream (files, etcetera).
* Does not affect performance to any non-`display` I/O.
* Can be configured independent of the REPL (e.g. the same configuration can be used for IJulia etc), and works in both `startup.jl` and a running REPL (or IJulia etc) session. *But* if you somehow have multiple REPLs running in parallel from the same Julia process, they can still change their display formats independently.
* Is extensible to floating-point types in other packages (opt-in).
This PR adds two new functions exported by the `InteractiveUtils` stdlib (which is loaded in REPL and IJulia sessions):
```jl
set_display_digits!([display], [T=AbstractFloat], digits; compact=false)
unset_display_digits!([display], [T=AbstractFloat]; compact=false)
```
to set the displayed precision (corresponding to a format string `"%.$(digits)g"`) for the type `T` (defaulting to `AbstractFloat`, i.e. all supported floating-point types). If you don't pass a `display` argument, it affects all currently loaded `AbstractDisplay`s (that support this settting) as well as a dictionary of defaults that is used for subsequently created REPL sessions (or IJulia sessions, in the future). By default, it affects the digits shown for all displays, but you can separately control the precision shown in `:compact=>true` `IOContext`s by passing the `compact=true` argument.
Internally, a supporting display (currently just `REPLDisplay`, but IJulia can opt-in to this later) implements this by wrapping its `IO` stream in an `InteractiveUtils.digitsio(io)` stream, which overloads `show` for the built-in `AbstractFloat` types (and external packages can opt-in for their own `AbstractFloat` types in the future). This way it has zero effect on any other `IO` stream. (`IOContext` wrappers around a `DigitsIO` stream inherit its floating-point display too.)
For example:
```jl
julia> z = rand(ComplexF64)
0.7230118898045041 + 0.33728384864961525im
julia> set_display_digits!(8)
julia> z
0.72301189 + 0.33728385im
julia> A = rand(2,2) # compact display uses 8 digits too
2×2 Matrix{Float64}:
0.34100692 0.21678433
0.23189211 0.68572311
julia> set_display_digits!(2, compact=true)
julia> A # compact display now uses 2 digits
2×2 Matrix{Float64}:
0.34 0.22
0.23 0.69
julia> z # non-compact display still uses 8 digits
0.72301189 + 0.33728385im
```
Before I do any more work on this, however, I want to get some feedback on the overall design. To do:
- [ ] Documentation
- [ ] Tests
There is also the question of whether we only want to control digits (always format as `%.*g`) or if we want a configurable format string (con: more complex; pro: more options, but is it actually useful in practice?). However, configurable format strings could be added later on if desired — you'd still want a simple API where you just specify the precision, as well as a more advanced API where you pass a format string.
23 Likes