Skip to content

trananhtung/numan

Repository files navigation

numan

All Contributors

Crates.io Documentation CI License no_std

Humanize numbers in Rust — turn raw numbers into the short, friendly strings you see in dashboards, CLIs and reports, and parse them back.

assert_eq!(numan::compact(1_234),       "1.2K");
assert_eq!(numan::word(1_200_000),      "1.2 million");
assert_eq!(numan::metric(1_500),        "1.5 k");
assert_eq!(numan::ordinal(22),          "22nd");
assert_eq!(numan::parse("1.5M").unwrap(), 1_500_000.0);

Zero dependencies. #![no_std] (needs only alloc). One small, focused crate.

Why numan?

Rust's ecosystem already humanizes file sizes (humansize) and relative time (chrono-humanize). What was missing is the part Python's humanize and Go's go-humanize are famous for: shortening the magnitude of plain numbers.

Need Reach for
File sizes (1.2 MiB) humansize
Relative time (3 hours ago) chrono-humanize
Number → words (forty-two) num2words
Locale thousands grouping num-format
Compact / word / metric magnitude (1.2K, 1.2 million, 1.2 k) + parsing numan

Install

[dependencies]
numan = "0.1"

Usage

Compact, word and metric notation

use numan::{compact, word, metric};

assert_eq!(compact(950),         "950");
assert_eq!(compact(12_300),      "12.3K");
assert_eq!(compact(1_500_000),   "1.5M");
assert_eq!(compact(-2_000_000),  "-2M");

assert_eq!(word(2_500),          "2.5 thousand");
assert_eq!(word(1_000_000),      "1 million");

assert_eq!(metric(1_000),        "1 k");   // SI: lowercase kilo
assert_eq!(metric(2_200_000),    "2.2 M");

Ordinals

assert_eq!(numan::ordinal(1),   "1st");
assert_eq!(numan::ordinal(112), "112th");

Parsing back

use numan::parse;

assert_eq!(parse("1.2K").unwrap(),       1_200.0);
assert_eq!(parse("1.5 million").unwrap(), 1_500_000.0);
assert_eq!(parse("-2.5bn").unwrap(),     -2_500_000_000.0);
assert_eq!(parse("1,234").unwrap(),       1_234.0); // separators ignored

Great for CLI flags like --limit 1.5M.

Fine-grained control with Formatter

use numan::Formatter;

let f = Formatter::compact().precision(2).space(true);
assert_eq!(f.format(1_234_567), "1.23 M");

// Keep trailing zeros, force an explicit sign:
let f = Formatter::compact().trim_zeros(false).sign_plus(true);
assert_eq!(f.format(1_000_000), "+1.0M");
Builder method Default Effect
precision(n) 1 Max fractional digits
trim_zeros(bool) true Drop trailing 0s and a dangling .
space(bool) per-notation Space between number and suffix
sign_plus(bool) false Prefix non-negatives with +

Behavior notes

  • Rounding uses Rust's standard round-half-to-even, and correctly carries into the next suffix (compact(999_950)"1M", never "1000K").
  • Beyond the table, compact (above a trillion) falls back to scientific notation (compact(1e36)"1e36"), which still round-trips through parse.
  • Non-finite floats render as "NaN", "inf", "-inf".
  • Negative zero (and negatives that round to zero) render as "0" — never "-0".
  • In parse, m/M means million (compact convention), never SI milli; SI exa (E) is not parsed, to avoid clashing with exponent notation (1e3), so metric output at exa returns an error rather than a wrong value.

no_std

numan is #![no_std] and only needs alloc (for String). It builds for bare-metal targets such as thumbv7em-none-eabi.

Contributors ✨

This project follows the all-contributors specification. Contributions of any kind are welcome — code, docs, bug reports, ideas, reviews! See the emoji key for how each contribution is recognized, and open a PR or issue to get involved.

Thanks goes to these wonderful people:

Tung Tran
Tung Tran

💻 🚧

License

Licensed under either of Apache-2.0 or MIT at your option.

About

Humanize numbers in Rust: compact (1.2K), word (1.2 million), SI metric (1.2 k) notation + parsing. Zero-dep, no_std.

Topics

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages