human_format
Rust Port of human-format from node, formatting numbers for us, while the machines are still at bay.
| Main | Develop |
|---|---|
human_format is a small Rust library that makes numbers easier for people to read. It converts large or small numbers into short strings, like turning 1_000_000 into “1.00 M”.
What it does
- Formats numbers with familiar suffixes:
k,M,G, and so on. - Parses human-friendly strings back into numbers with
try_parse. - Supports the newest SI prefixes (
R,Q,r,q). - Accepts and (optionally) outputs the micro sign
µ. - Lets you force a specific suffix for output (for example, always show values in
M). - Includes a
Scales::Time()option for common time units (seconds, minutes, hours, days, months, years, quarters, centuries, etc.).
Quick start
Add the crate:
cargo add human_format
Use the formatter:
use human_format::Formatter;
// Default SI formatting
let s = Formatter::new().format(1000.0);
assert_eq!(s, "1.00 k");
// Control decimals
let s = Formatter::new().with_decimals(1).format(1337.0);
assert_eq!(s, "1.3 k");
Use binary scales (base 1024):
use human_format::{Formatter, Scales};
let s = Formatter::new().with_scales(Scales::Binary()).format(1024.0);
assert_eq!(s, "1.00 ki");
Parsing strings
Convert a human-friendly string back to a f64 with try_parse.
use human_format::{Formatter, Scales};
let f = Formatter::new();
assert_eq!(f.try_parse("1.00 k").unwrap(), 1000.0);
let mut fb = Formatter::new();
fb.with_scales(Scales::Binary());
assert_eq!(fb.try_parse("1.00 ki").unwrap(), 1024.0);
The parser accepts the micro sign µ as input.
Force a suffix in output
To force output to a certain suffix, use with_suffix. The value is scaled to match that suffix if possible.
use human_format::Formatter;
let mut f = Formatter::new();
f.with_suffix("M");
assert_eq!(f.format(100_000.0), "0.10 M");
If the suffix is not valid for the current Scales, the formatter falls back to automatic selection.
Micro sign output
Use with_micro_sign(true) to show µ for micro values in the output.
use human_format::Formatter;
let mut f = Formatter::new();
f.with_micro_sign(true);
assert_eq!(f.format(0.000001_f64), "1.00 µ");
Parsing accepts both u and µ.
Time scales
Scales::Time() uses a set of explicit unit multipliers for time. It uses average values where needed (for example, the average year is 365.2425 days). Use Scales::Time() when you want time-aware formatting and parsing.
use human_format::{Formatter, Scales};
let mut ft = Formatter::new();
ft.with_scales(Scales::Time());
// 90 seconds -> 1.50 m (minutes)
assert_eq!(ft.format(90.0), "1.50 m");
// Quarters (qtr) parse as three-month periods
assert!(ft.try_parse("1 qtr").is_ok());
For more examples please consult tests/demo.rs
Notes
- Months and years are approximate here. For precise calendar math, use a date-time library.
- Very large and very small numbers can lose precision when using
f64. - The
with_suffixmethod uses the same suffix strings thattry_parseaccepts.
Suffix vs unit ambiguity
Some short tokens can be ambiguous. For example, the single letter m can mean milli (a magnitude suffix) or meter (a measurement unit) depending on how you configure the Formatter:
Formatter::new().with_suffix("m")treatsmas milli (scale).Formatter::new().with_units("m")appendsmas the units string (meter).
To avoid ambiguity, prefer longer suffixes or specify units explicitly with with_units.
Case sensitivity
Suffix matching is case-sensitive: M (mega) is not the same as m (milli). If you want different aliases or case-insensitive parsing, add those aliases to the Scales you use.
parse_or_clamp behavior
The parse_or_clamp helper can be used when accepting user-provided suffixes that may be unknown or misspelled. Call it with clamp = true to interpret unknown suffixes as the largest available magnitude in the active Scales.
Important: when a Scales provides an explicit_map (for example Scales::Time()), parse_or_clamp will clamp to the largest multiplier defined in that explicit map rather than assuming a power-of-base value. This ensures consistent behavior for non-power-of-base scales such as time units (where the largest defined unit is Gyr).
Testing and correctness notes
We added a number of focused tests to ensure robust handling of edge-cases:
- Micro sign parsing and optional micro-sign output (tests/micro_sign.rs).
- Forced-suffix behavior including unknown-suffix fallbacks (tests/forced_suffix.rs).
- SI round-trip formatting for new prefixes
R/Qand parsing validation (tests/si_roundtrip.rs). - Time scale edge cases: months, quarters, parsing case-sensitivity, and clamp behavior (tests/time_edgecases.rs).
If you rely on case-insensitive parsing or custom aliases, construct a Scales with the aliases you need and pass it to Formatter::with_scales.
Contributing
Contributions are welcome.
When you add features, please include tests.
License
See the repository LICENSE file for license details.