#si-units #units #numbers #pretty #unit #human

si-scale

使用适当的SI尺度格式化值:秒(1.3e-5) -> 13 µs

10个版本

0.2.3 2024年8月16日
0.2.2 2023年11月8日
0.2.1 2022年8月19日
0.1.5 2022年6月26日
0.1.3 2021年10月21日

#21 in 值格式化

Download history 632/week @ 2024-05-03 691/week @ 2024-05-10 739/week @ 2024-05-17 526/week @ 2024-05-24 336/week @ 2024-05-31 579/week @ 2024-06-07 435/week @ 2024-06-14 491/week @ 2024-06-21 903/week @ 2024-06-28 814/week @ 2024-07-05 387/week @ 2024-07-12 730/week @ 2024-07-19 780/week @ 2024-07-26 556/week @ 2024-08-02 601/week @ 2024-08-09 860/week @ 2024-08-16

2,969 每月下载量
用于 3 crates

MIT/Apache

94KB
1K SLoC

si-scale

crate documentation minimum rustc 1.74 build status

根据SI(国际单位制)格式化值。

版本要求:rustc 1.74+

[dependencies]
si-scale = "0.2"

概述

此crate使用SI尺度格式化数字:从1 y(yocto,即1e-24)到1 Y(Yotta,即1e24)。

它与伟大的human-repr具有相同的目的,但平衡不同

  • 此crate在调用位置产生更简洁的代码
  • 它赋予你对输出的更多控制权。如本页稍后所示,您可以轻松扩展它以处理吞吐量等(认真地说,请见下文)
  • 但它只作用于数字,因此它不会阻止您使用函数在持续时间值上打印米(而human-repr做得很好)。

入门

要使用此crate,请使用以下预定义辅助函数之一,或自行构建。

基本示例

use si_scale::helpers::{seconds, seconds3};

let actual = format!("{}", seconds(1.3e-5));
let expected = "13 µs";
assert_eq!(actual, expected);

let actual = format!("{}", seconds3(1.3e-5));
let expected = "13.000 µs";
assert_eq!(actual, expected);

预定义辅助函数

辅助函数使用以下命名约定

  • 名称表示要使用的单位
  • 数字后缀表示浮点数的十进制位数
  • 下划线后缀表示使用“千位分组”的数字

但您在编写自己的函数时可以偏离这一点。

目前辅助函数有

辅助函数 输入 输出
number_() 1.234567, 1515 1.234_567, 1_515
--- --- ---
() 1.234567e-6, 16e-3 1.234567 µs, 16 ms
秒数3() 1.234567e-6, 16e-3 1.235 µs, 16.000 ms
--- --- ---
字节() 1234567 1.234567 MB
字节_() 1234567 1_234_567B
字节1() 2.3 *1e12 2.3 TB
字节2() 2.3 *1e12 2.30 TB
--- --- ---
bi字节() 1024 * 1024 * 1.25 1.25MiB
bi字节1() 1024 * 1024 * 1.25 1.3MiB
bi字节2() 1024 * 1024 * 1.25 1.25MiB

自定义辅助函数 - BYOU (自带单位)

要定义自己的格式函数,请使用 scale_fn!() 宏。此crate中所有预定义的辅助函数都是使用此宏定义的。

辅助函数 尾数 前缀约束 基数 分组 输入 输出
number_() "{}" 仅单位 B1000 _ 1.234567, 1515 1.234_567, 1_515
--- -- --- --- --- --- ---
() "{}" 单位和以下 B1000 1.234567e-6, 16e-3 1.234567 µs, 16 ms
秒数3() "{:.3}" 单位和以下 B1000 1.234567e-6, 16e-3 1.235 µs, 16.000 ms
--- -- --- --- --- --- ---
字节() "{}" 单位和以上 B1000 1234567 1.234567 MB
字节_() "{}" 仅单位 B1000 _ 1234567 1_234_567B
字节1() "{:.1}" 单位和以上 B1000 2.3 *1e12 2.3 TB
字节2() "{:.2}" 单位和以上 B1000 2.3 *1e12 2.30 TB
--- -- --- --- --- --- ---
bi字节() "{}" 单位和以上 B1024 1024 * 1024 * 1.25 1.25MiB
bi字节1() "{:.1}" 单位和以上 B1024 1024 * 1024 * 1.25 1.3MiB
bi字节2() "{:.2}" 单位和以上 B1024 1024 * 1024 * 1.25 1.25MiB

附加表列显示了底层控件。

"尾数"列

它是一个格式字符串,仅在缩放后对尾数起作用。例如,"{}" 将显示所有数字的值或如果它是一个整数则没有数字,而例如 "{:.1}" 将始终显示一个小数。

"前缀约束"列

简而言之,这允许以令人惊讶的缩放表示值:例如,你永远不会写 1.2 ksec,但总是 1200 sec1.2e3 sec。同样,你永远不会写 2 mB,但总是 0.002 B2e-3 B

因此,这里的“单位”一词指的是单位缩放(1),与测量单位无关。它限制了值的可能缩放

  • UnitOnly 表示提供的值不会缩放:如果你提供一个大于1000的值,比如说1234,它将被打印为1234。
  • UnitAndAbove 表示提供的值只能使用更高的缩放,例如 16 GB 但永远不能是 4.3 µB
  • UnitAndBelow 表示提供的值只能使用较低的缩放,例如 1.3 µsec 但不能是 16 Gsec

"基数"列

基数B1000表示1k = 1000,基数B1024表示1k = 1024。这在IEC文档中定义。如果你将基数设置为B1024,尾数将适当地缩放,但在大多数情况下,你将使用B1000

"分组"列

分组是指“千位分组”;提供的字符将被使用(例如,1234显示为1_234),如果没有提供,则值将显示为1234。

示例 - 如何定义kibits/s的辅助函数

例如,让我们定义一个每秒比特数的格式化函数,该函数以两位小数打印尾数,并使用1024为基数(其中1千比特=1024)。请注意,尽管我们在单独的模块中定义了此函数,但这不是必需的。

mod unit_fmt {
    use si_scale::scale_fn;
    use si_scale::prelude::Value;

    // defines the `bits_per_sec()` function
    scale_fn!(bits_per_sec,
              base: B1024,
              constraint: UnitAndAbove,
              mantissa_fmt: "{:.2}",
              groupings: '_',
              unit: "bit/s",
              doc: "Return a string with the value and its si-scaled unit of bit/s.");
}

use unit_fmt::bits_per_sec;

fn main() {
    let x = 2.1 * 1024 as f32;
    let actual = format!("throughput: {:>15}", bits_per_sec(x));
    let expected = "throughput:    2.10 kibit/s";
    assert_eq!(actual, expected);

    let x = 2;
    let actual = format!("throughput: {}", bits_per_sec(x));
    let expected = "throughput: 2.00 bit/s";
    assert_eq!(actual, expected);
}

您可以通过省略宏的groupings参数来不分隔千位。

SI单位换算 - 开发者文档

基数=1000时,1千=1000,1兆=1_000_000,1毫=0.001,1微=0.000_001,等等。

最小值(包含) 最大值(不包含) 量级 前缀
.. .. -24 前缀::尧托
.. .. -21 前缀::泽托
.. .. -18 前缀::阿托
.. .. -15 前缀::飞托
.. .. -12 前缀::皮科
.. .. -9 前缀::纳诺
0.000_001 0.001 -6 前缀::微米
0.001 1 -3 前缀::毫微
1 1_000 0 前缀::单位
1000 1_000_000 3 前缀::
1_000_000 1_000_000_000 6 前缀::
.. .. 9 前缀::
.. .. 12 前缀::
.. .. 15 前缀::
.. .. 18 前缀::
.. .. 21 前缀::
.. .. 24 前缀::

基数通常是1000,但也可以是1024(比字节)。

基数=1024时,1千比特=1024,1兆比特=1024 * 1024,等等。

API概述

中心表示形式是Value类型,它包含

  • 尾数,
  • SI单位前缀(如“千”,“兆”等),
  • 以及基数,它表示“1千”表示1000(最常见)和“1千”表示1024(对于kiB,MiB等)的情况。

此crate提供2个API:一个低级API和一个高级API,方便使用。

对于低级API,典型用例是

  • 首先将数字解析为Value。为此,您必须指定基数,并可能对SI尺度施加某些约束。请参阅Value::new()Value::new_with()

  • 然后通过自行格式化尾数和前缀(实现fmt::Display特质)或使用提供的格式化程序来显示Value

对于高级API,典型用例是

  1. 使用提供的函数解析并显示数字,例如bibytes()bytes()seconds(),它们将为每个数字选择最合适的SI尺度。

  2. 如果您希望获得与低级API相同的控制粒度(例如,以某种方式约束尺度、使用某些基数、特定的尾数格式化),则可以使用提供的宏scale_fn!()构建自定义函数。例如,如bibytes()bytes()seconds()等现有函数都是使用此相同宏构建的。

高级API

函数 seconds3() 将一个数字解析为一个 Value,并使用三位小数和适用于秒的适当比例(UnitAndBelow)进行显示,这样就不会输出诸如千秒之类的无意义比例。函数 seconds() 执行相同的操作,但使用默认的格式("{}")来格式化尾数,因此对于整数值的尾数不打印小数。

use si_scale::helpers::{seconds, seconds3};

let actual = format!("result is {:>15}", seconds(1234.5678));
let expected = "result is     1234.5678 s";
assert_eq!(actual, expected);

let actual = format!("result is {:>10}", seconds3(12.3e-7));
let expected = "result is   1.230 µs";
assert_eq!(actual, expected);

函数 bytes() 以基数 1000 将一个数字解析为一个 Value,并使用一位小数和适用于字节的适当比例(UnitAndAbove)进行显示,这样就不会出现诸如毫字节之类的无意义比例。

use si_scale::helpers::{bytes, bytes1};

let actual = format!("result is {}", bytes1(12_345_678));
let expected = "result is 12.3 MB";
assert_eq!(actual, expected);

let actual = format!("result is {:>10}", bytes(16));
let expected = "result is       16 B";
assert_eq!(actual, expected);

let actual = format!("result is {}", bytes(0.12));
let expected = "result is 0.12 B";
assert_eq!(actual, expected);

函数 bibytes1() 以基数 1024 将一个数字解析为一个 Value,并使用一位小数和适用于字节的适当比例(UnitAndAbove)进行显示,这样就不会出现诸如毫字节之类的无意义比例。

use si_scale::helpers::{bibytes, bibytes1};

let actual = format!("result is {}", bibytes1(12_345_678));
let expected = "result is 11.8 MiB";
assert_eq!(actual, expected);

let actual = format!("result is {}", bibytes(16 * 1024));
let expected = "result is 16 kiB";
assert_eq!(actual, expected);

let actual = format!("result is {:>10}", bibytes1(16));
let expected = "result is     16.0 B";
assert_eq!(actual, expected);

let actual = format!("result is {}", bibytes(0.12));
let expected = "result is 0.12 B";
assert_eq!(actual, expected);

低级 API

使用 Value::new() 创建 Value

低级函数 Value::new() 将任何可转换为 f64 的数字转换为使用基数 1000 的 Value。结构体 Value 实现了 From 以处理常见数字,并将它们委托给 Value::new(),因此在实践中它们是等价的。以下是一些示例。

use std::convert::From;
use si_scale::prelude::*;

let actual = Value::from(0.123);
let expected = Value {
    mantissa: 123f64,
    prefix: Prefix::Milli,
    base: Base::B1000,
};
assert_eq!(actual, expected);
assert_eq!(Value::new(0.123), expected);

let actual: Value = 0.123.into();
assert_eq!(actual, expected);

let actual: Value = 1300i32.into();
let expected = Value {
    mantissa: 1.3f64,
    prefix: Prefix::Kilo,
    base: Base::B1000,
};
assert_eq!(actual, expected);

let actual: Vec<Value> = vec![0.123f64, -1.5e28]
    .iter().map(|n| n.into()).collect();
let expected = vec![
    Value {
        mantissa: 123f64,
        prefix: Prefix::Milli,
        base: Base::B1000,
    },
    Value {
        mantissa: -1.5e4f64,
        prefix: Prefix::Yotta,
        base: Base::B1000,
    },
];
assert_eq!(actual, expected);

如最后一个示例所示,对于超出 SI 前缀的比例值,使用最接近的 SI 前缀来表示。

使用 Value::new_with() 创建 Value

低级 Value::new_with()Value::new() 类似,但也期望一个基数和您想要使用的比例约束。与简单的 Value::new() 相比,这允许使用基数 1024 的比例(例如 kiB、MiB 等)并防止秒的更高比例或字节等整数量单位的更低比例(例如,避免将 1300 秒写成 1.3 ks 或将 0.415 B 写成 415 mB)。

use si_scale::prelude::*;

// Assume this is seconds, no kilo-seconds make sense.
let actual = Value::new_with(1234, Base::B1000, Constraint::UnitAndBelow);
let expected = Value {
    mantissa: 1234f64,
    prefix: Prefix::Unit,
    base: Base::B1000,
};
assert_eq!(actual, expected);

不用担心冗长,以下解析器有助于解决这个问题。

格式化值

在这个示例中,数字 x 被转换为值并使用最合适的 SI 前缀进行显示。用户选择将前缀约束为小于 Unit(1),因为千秒没有意义。

use si_scale::format_value;
use si_scale::{value::Value, base::Base, prefix::Constraint};

let x = 1234.5678;
let v = Value::new_with(x, Base::B1000, Constraint::UnitAndBelow);
let unit = "s";

let actual = format!(
    "result is {}{u}",
    format_value!(v, "{:.5}", groupings: '_'),
    u = unit
);
let expected = "result is 1_234.567_80 s";
assert_eq!(actual, expected);

运行代码覆盖率

安装 llvm-tools-preview 组件和 grcov

rustup component add llvm-tools-preview
cargo install grcov

安装夜间版本

rustup toolchain install nightly

以下make调用将切换到夜间版本运行测试,并使用Cargo输出HTML覆盖率报告到./coverage/

make coverage

覆盖率报告位于./coverage/index.html

许可证

根据您的选择,许可协议为以下之一

贡献

除非您明确声明,否则任何有意提交以供包含在作品中的贡献,如Apache-2.0许可证中定义的,将根据上述方式双重许可,不附加任何额外条款或条件。

无运行时依赖