IMPORTANT: all of this package’s functionalities are being merged into Formatting.jl, please use that instead
A way to get around the limitation that @sprintf
has to take a literal string argument.
The core part is basically a c-style print formatter using the standard
@sprintf
macro.
It also adds functionalities such as commas separator (thousands), parenthesis for negatives,
stripping trailing zeros, and mixed fractions.
The idea here is that the package compiles a function only once for each unique
format string within the NumFormat.*
name space, so repeated use is faster.
Unrelated parts of a session using the same format string would reuse the same
function, avoiding redundant compilation. To avoid the proliferation of
functions, we limit the usage to only 1 argument. Practical consideration
would suggest that only dozens of functions would be created in a session, which
seems manageable.
Usage
using NumFormat
fmt = "%10.3f"
s = sprintf1( fmt, 3.14159 ) # usage 1. Quite performant. Easiest to switch to.
fmtrfunc = generate_formatter( fmt ) # usage 2. This bypass repeated lookup of cached function. Most performant.
s = fmtrfunc( 3.14159 )
s = format( 3.14159, precision=3 ) # usage 3. Most flexible, with some non-printf options. Least performant.
Put the macro in a quote block and eval it (very slow)
fmt = "%10d"
n = 1234
s = eval( Expr( :macrocall, symbol( "@sprintf" ), fmt, n ) ) # VERY slow, 1000x penalty
ccall
to libc sprintf. See this gist. The
example shows 6-7x speed penalty.
Set up a lambda with the macro inside. Ok for repeated use. But the lambda
goes out of scope quickly so it cannot be reused. @eval
would also repeat
compilation, even for the same format.
fmt = "%10d"
n = 1234
l = :( x -> x ) # placeholder lambda l.args[2].args[2] = Expr( :macrocall, symbol( "@sprintf" ), fmt, :x ) mfmtr = eval(l)
@eval mfmtr(x) = @sprintf($fmt,x)
s = mfmtr( n ) # quite fast, but the definition is clunky
## Speed
`sprintf1`: Speed penalty is about 20% for floating point and 30% for integers.
If the formatter is stored and used instead (see the example using `generate_formatter` above),
the speed penalty reduces to 10% for floating point and 15% for integers.
## Commas
This package also supplements the lack of thousand separator e.g. `"%'d"`, `"%'f"`, `"%'s"`.
Note: `"%'s"` behavior is that for small enough floating point (but not too small),
thousand separator would be used. If the number needs to be represented by `"%e"`, no
separator is used.
## Flexible `format` function
This package contains a run-time number formatter `format` function, which goes beyond
the standard `sprintf` functionality.
An example:
s = format( 1234, commas=true ) # 1,234 s = format( -1234, commas=true, parens=true ) # (1,234)
The keyword arguments are (Bold keywards are not printf standard)
* width. Integer. Try to fit the output into this many characters. May not be successful.
Sacrifice space first, then commas.
* precision. Integer. How many decimal places.
* leftjustified. Boolean
* zeropadding. Boolean
* commas. Boolean. Thousands-group separator.
* signed. Boolean. Always show +/- sign?
* positivespace. Boolean. Prepend an extra space for positive numbers? (so they align nicely with negative numbers)
* **parens**. Boolean. Use parenthesis instead of "-". e.g. `(1.01)` instead of `-1.01`. Useful in finance. Note that
you cannot use `signed` and `parens` option at the same time.
* **stripzeros**. Boolean. Strip trailing '0' to the right of the decimal (and to the left of 'e', if any ).
* It may strip the decimal point itself if all trailing places are zeros.
* This is true by default if precision is not given, and vice versa.
* alternative. Boolean. See `#` alternative form explanation in standard printf documentation
* conversion. length=1 string. Default is type dependent. It can be one of `aAeEfFoxX`. See standard
printf documentation.
* **mixedfraction**. Boolean. If the number is rational, format it in mixed fraction e.g. `1_1/2` instead of `3/2`
* **mixedfractionsep**. Default `_`
* **fractionsep**. Default `/`
* **fractionwidth**. Integer. Try to pad zeros to the numerator until the fractional part has this width
* **tryden**. Integer. Try to use this denominator instead of a smaller one. No-op if it'd lose precision.
See the test script for more examples.
11/21/2014
about 6 years ago
22 commits