Number Formatter of Fixed Significance with Metric or Binary Prefix
Formats a given number in one of the three Signifix notations as defined below by determining
Three notations are defined,
The two Signifix notations with metric prefix comprise
±1.000
to ±999.9
to cover the three powers of ten of a particular
metric prefix with the three different decimal mark positions between
these four figures, and±1.234␣k
,
that is the default notation,±1k234
,
that is the alternate notation.In default notation the placeholder is another whitespace as in ±1.234␣␣
to align consistently, while in alternate notation it is a number sign as in
±1#234
to conspicuously separate the integer from the fractional part of
the significand.
The plus sign of positive numbers is optional.
The one Signifix notation with binary prefix comprises
±1.000
over ±999.9
to ±1 023
to cover the four powers of ten of a
particular binary prefix with the three different decimal mark positions
between these four figures and a thousands separator, and±1.234␣Ki
.To align consistently, the placeholder is another two whitespaces as in
±1.234␣␣␣
while the thousands separator is a whitespace as in ±1␣023␣Ki
.
The plus sign of positive numbers is optional.
This crate is on crates.io and can be
used by adding signifix
to the dependencies in your project's
Cargo.toml
:
toml
[dependencies]
signifix = "0.6.0"
and this to your crate root:
```rust
extern crate signifix; ```
The Signifix notations result in a fixed number of characters preventing jumps to the left or right while making maximum use of their occupied space:
```rust use std::convert::TryFrom; // Until stabilized.
use signifix::{metric, binary, Result};
let metric = |number| -> Result<(String, String)> {
let number = metric::Signifix::tryfrom(number)?;
Ok((format!("{}", number), format!("{:#}", number)))
};
let binary = |number| -> Result
// Three different decimal mark positions covering the three powers of ten // of a particular metric prefix. asserteq!(metric(1E-04), Ok(("100.0 µ".into(), "100µ0".into()))); // 3rd asserteq!(metric(1E-03), Ok(("1.000 m".into(), "1m000".into()))); // 1st asserteq!(metric(1E-02), Ok(("10.00 m".into(), "10m00".into()))); // 2nd asserteq!(metric(1E-01), Ok(("100.0 m".into(), "100m0".into()))); // 3rd asserteq!(metric(1E+00), Ok(("1.000 ".into(), "1#000".into()))); // 1st asserteq!(metric(1E+01), Ok(("10.00 ".into(), "10#00".into()))); // 2nd asserteq!(metric(1E+02), Ok(("100.0 ".into(), "100#0".into()))); // 3rd asserteq!(metric(1E+03), Ok(("1.000 k".into(), "1k000".into()))); // 1st asserteq!(metric(1E+04), Ok(("10.00 k".into(), "10k00".into()))); // 2nd asserteq!(metric(1E+05), Ok(("100.0 k".into(), "100k0".into()))); // 3rd assert_eq!(metric(1E+06), Ok(("1.000 M".into(), "1M000".into()))); // 1st
// Three different decimal mark positions and a thousands separator covering // the four powers of ten of a particular binary prefix. asserteq!(binary(1024f64.powi(0) * 1E+00), Ok("1.000 ".into())); // 1st asserteq!(binary(1024f64.powi(0) * 1E+01), Ok("10.00 ".into())); // 2nd asserteq!(binary(1024f64.powi(0) * 1E+02), Ok("100.0 ".into())); // 3rd asserteq!(binary(1024f64.powi(0) * 1E+03), Ok("1 000 ".into())); // 4th asserteq!(binary(1024f64.powi(1) * 1E+00), Ok("1.000 Ki".into())); // 1st asserteq!(binary(1024f64.powi(1) * 1E+01), Ok("10.00 Ki".into())); // 2nd asserteq!(binary(1024f64.powi(1) * 1E+02), Ok("100.0 Ki".into())); // 3rd asserteq!(binary(1024f64.powi(1) * 1E+03), Ok("1 000 Ki".into())); // 4th asserteq!(binary(1024f64.powi(2) * 1E+00), Ok("1.000 Mi".into())); // 1st
// Rounding over prefixes is safe against floating-point inaccuracies. asserteq!(metric(999.9499999999998), Ok(("999.9 ".into(), "999#9".into()))); asserteq!(metric(999.9499999999999), Ok(("1.000 k".into(), "1k000".into()))); asserteq!(binary(1023.49999999999994), Ok("1 023 ".into())); asserteq!(binary(1023.49999999999995), Ok("1.000 Ki".into())); ```
This is useful to smoothly refresh a transfer rate within a terminal:
```rust
use std::convert::TryFrom; // Until stabilized.
use std::f64; use signifix::metric::{Signifix, Error, DEFMINLEN};
let formatrate = |bytes: u128, nanoseconds: u128| -> String { let bytespersecond = bytes as f64 / nanoseconds as f64 * 1E+09; let unit = "B/s"; let rate = match Signifix::tryfrom(bytespersecond) { Ok(rate) => if rate.factor() < 1E+00 { " - slow - ".into() // instead of mB/s, µB/s, ... } else { format!("{}{}", rate, unit) // normal rate }, Err(case) => match case { Error::OutOfLowerBound(rate) => if rate == 0f64 { " - idle - " // no progress at all } else { " - slow - " // almost no progress }, Error::OutOfUpperBound(rate) => if rate == f64::INFINITY { " - ---- - " // zero nanoseconds } else { " - fast - " // awkwardly fast }, Error::Nan => " - ---- - ", // zero bytes in zero nanoseconds }.into(), }; asserteq!(rate.chars().count(), DEFMIN_LEN + unit.chars().count()); rate };
asserteq!(formatrate(42667, 300000000000), "142.2 B/s"); asserteq!(formatrate(42667, 030000000000), "1.422 kB/s"); asserteq!(formatrate(42667, 003000000000), "14.22 kB/s"); asserteq!(formatrate(00001, 003000000000), " - slow - "); asserteq!(formatrate(00000, 003000000000), " - idle - "); asserteq!(formatrate(42667, 000000000000), " - ---- - "); ```
Or to monitor a measured quantity like an electrical current including its direction with positive numbers being padded to align with negative ones:
```rust use std::convert::TryFrom; // Until stabilized.
use signifix::metric::{Signifix, Result, DEFMAXLEN};
let formatload = |amps| -> Result
asserteq!(formatload(Some( 1.476E-06)), Ok(" 1.476 µA".into())); asserteq!(formatload(None), Ok(" 0 A".into())); asserteq!(formatload(Some(-2.927E-06)), Ok("-2.927 µA".into())); ```
While to visualize a change in file size, a plus sign might be preferred for positive numbers:
```rust use std::convert::TryFrom; // Until stabilized.
use signifix::metric::{Signifix, Error, Result};
let formatdiff = |curr, prev| -> Result
asserteq!(formatdiff(78346, 57393), Ok("+20k95".into())); asserteq!(formatdiff(93837, 93837), Ok("=const".into())); asserteq!(formatdiff(27473, 36839), Ok("-9k366".into())); ```
The binary prefix instead suits well to visualize quantities being multiples of powers of two, such as memory boundaries due to binary addressing:
```rust use std::convert::TryFrom; // Until stabilized.
use signifix::binary::{Signifix, Error, Result};
let formatused = |used: usize, size: usize| -> Result
asserteq!(formatused(0000usize.pow(1), 1024usize.pow(3)), Ok(" 0 B ( 0 %) of 1.000 GiB)".into())); asserteq!(formatused(1024usize.pow(2), 1024usize.pow(3)), Ok("1.000 MiB ( < 1 %) of 1.000 GiB)".into())); asserteq!(formatused(3292usize.pow(2), 1024usize.pow(3)), Ok("10.34 MiB (1.009 %) of 1.000 GiB)".into())); asserteq!(formatused(8192usize.pow(2), 1024usize.pow(3)), Ok("64.00 MiB (6.250 %) of 1.000 GiB)".into())); asserteq!(formatused(1000usize.pow(3), 1024usize.pow(3)), Ok("953.7 MiB (93.13 %) of 1.000 GiB)".into())); asserteq!(formatused(1024usize.pow(3), 1024usize.pow(3)), Ok("1.000 GiB (100.0 %) of 1.000 GiB)".into())); ```
Copyright (c) 2016, 2017 Rouven Spreckels n3vu0r@qu1x.org
Usage of the works is permitted provided that this instrument is retained with the works, so that any entity that uses the works is notified of this instrument.
DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY.