### 1 unstable release

0.1.0 | Apr 20, 2020 |
---|

#**7** in #representation

**1,715** downloads per month

Used in **13** crates
(6 directly)

**MIT/Apache**

57KB

905 lines

# format_num

Dynamic formatting of numbers into human readable forms.

Did you encounter cases where Rust doesn't represent numbers the way you expect?

`for` i `in` `1``..``=``10` `{`
`println!``(``"``{}``"``,` `0.``1` `*` i `as` `f64``)``;`
`}`

You get this:

`0.1
0.2
0.30000000000000004
0.4
0.5
0.6000000000000001
0.7000000000000001
0.8
0.9
1
`

That's actually not a Rust issue, but rather how floats are represented in binary.

Yet rounding error is not the only reason to customize number formatting. A table of numbers should be formatted consistently for comparison; above, 1.0 would be better than 1. Large numbers may need to have grouped digits (e.g. 42,000) or be in scientific or metric notation (4.2e+4, 42k). Reported numerical results should be rounded to significant digits (4021 becomes 4000) and so on.

The parser is modeled after Python 3's format specification mini-language (PEP3101) with some minor implementation details changes.

The general form of a format specifier is:

`[[fill]align][sign][symbol][0][width][,][.precision][type]
`

The *fill* can be any character. The presence of a fill character is signaled by the align
character following it, which must be one of the following:

- Forces the field to be right-aligned within the available space.`>`

- Forces the field to be left-aligned within the available space.`<`

- Forces the field to be centered within the available space.`^`

- like `=`

, but with any sign and symbol to the left of any padding.`>`

The *sign* can be:

- nothing for zero or positive and a minus sign for negative (default behavior).`-`

- a plus sign for zero or positive and a minus sign for negative.`+`

(space) - a space for zero or positive and a minus sign for negative.` `

The *symbol* can be:

The

option causes the “alternate form” to be used for the conversion. The alternate
form is defined differently for different types. For integers, when binary (`#`

), octal
(`b`

or `o`

), or hexadecimal (`O`

or `x`

) output is used, this option adds the prefix
respective "0b", "0o", "0O" or "0x" to the output value. For floats, the alternate form
causes the result of the conversion to always contain a decimal-point character,
even if no digits follow it.`X`

The zero (0) option enables zero-padding; this implicitly sets fill to 0 and align to =.

The *width* defines the minimum field width; if not specified, then the width will be
determined by the content.

The comma (,) option enables the use of a group separator, such as a comma for thousands.

Depending on the *type*, the *precision* either indicates the number of digits that follow
the decimal point (types

and `f`

), or the number of significant digits (types `%`

and `e`

). If the precision is not specified, it defaults to 6 for all types. Precision
is ignored for integer formats (types `s`

, `b`

, `o`

, `d`

and `x`

).`X`

The available *type* values are:

- exponent notation.`e`

- fixed point notation.`f`

- decimal notation with an SI prefix, rounded to significant digits.`s`

- multiply by 100, and then decimal notation with a percent sign.`%`

- binary notation, rounded to integer.`b`

- octal notation, rounded to integer.`o`

- decimal notation, rounded to integer.`d`

- hexadecimal notation, using lower-case letters, rounded to integer.`x`

- hexadecimal notation, using upper-case letters, rounded to integer.`X`

## Examples

`use` `format_num``::``{`NumberFormat`,` format_num`}``;`
`fn` `main``(``)`` ``{`
`let` num `=` `NumberFormat``::`new`(``)``;`
`let` format_spec `=` `"`+14d`"``;`
`assert_eq!``(`num`.``format``(``"`.1f`"``,` `0.``06``)``,` `"`0.1`"``)``;`
`assert_eq!``(`num`.``format``(``"`#.0f`"``,` `10.``1``)``,` `"`10.`"``)``;` `//` float alternate form (always show a decimal point)
`assert_eq!``(`num`.``format``(``"`#b`"``,` `3``)``,` `"`0b11`"``)``;`
`assert_eq!``(`num`.``format``(``"`b`"``,` `3``)``,` `"`11`"``)``;`
`assert_eq!``(`num`.``format``(`format_spec`,` `2_147_483_647``)``,` `"` +2147483647`"``)``;`
`//` Or, you can use a macro for simplicity
`assert_eq!``(``format_num!``(``"`#X`"``,` `48879``)``,` `"`0xBEEF`"``)``;`
`assert_eq!``(``format_num!``(``"`.2s`"``,` 42e6`)``,` `"`42M`"``)``;`
`assert_eq!``(``format_num!``(``"`.^20d`"``,` `12``)``,` `"`.........12.........`"``)``;` `//` dot filled and centered
`assert_eq!``(``format_num!``(``"`+10.0f`"``,` `255``)``,` `"` +255`"``)``;`
`assert_eq!``(``format_num!``(``"`.0%`"``,` `0.``123``)``,` `"`12%`"``)``;`
`assert_eq!``(``format_num!``(``"`+016,.2s`"``,` 42e12`)``,` `"`+000,000,000,042T`"``)``;` `//` grouped zero-padded with a mandatory sign, SI-prefixed with 2 significant digits
`}`

### Note

A current limitation is that the number to be formatted should implement the

trait. While this covers a broad range of use cases, for big numbers (>u64::MAX) some
precision will be lost.`Into``<``f64``>`

## Roadmap

Implementation of more types:

(general format), `g`

(number), `n`

(character).`c`

Also, there are plans to add support for locales and currency formatting.

## License

**format_num** is licensed under either of the Apache License, Version 2.0,
or the MIT license at your discretion.

#### Dependencies

~1–1.4MB

~38K SLoC