20 个版本

使用旧的 Rust 2015

0.4.5 2024年6月17日
0.4.3 2024年3月5日
0.4.2 2023年10月16日
0.4.1 2023年7月13日
0.0.7 2017年5月2日

#1 in 数学

Download history 197118/week @ 2024-05-03 208887/week @ 2024-05-10 212759/week @ 2024-05-17 199492/week @ 2024-05-24 210524/week @ 2024-05-31 211783/week @ 2024-06-07 208431/week @ 2024-06-14 211351/week @ 2024-06-21 209026/week @ 2024-06-28 220408/week @ 2024-07-05 195262/week @ 2024-07-12 206214/week @ 2024-07-19 215737/week @ 2024-07-26 215753/week @ 2024-08-02 229434/week @ 2024-08-09 229715/week @ 2024-08-16

933,225 次每月下载
用于 819 个 crates (261 直接)

MIT/Apache

300KB
6.5K SLoC

bigdecimal-rs

crate Documentation

minimum rustc 1.43

codecov build status - master build status - trunk

使用纯 Rust 实现的任意精度十进制数。

社区

在 Zulip 上加入讨论:https://bigdecimal-rs.zulipchat.com

请分享重要内容,如用例、问题、基准测试和命名约定偏好。

此项目目前正在重写,因此如果性能或灵活性不足,请稍后再查看,可能会有所修复。

使用方法

将 bigdecimal 添加为依赖项到您的 Cargo.toml 文件

[dependencies]
bigdecimal = "0.4"

导入并使用 BigDecimal 结构体来解决您的问题

use bigdecimal::BigDecimal;

fn main() {
    let two = BigDecimal::from(2);
    println!("sqrt(2) = {}", two.sqrt().unwrap());
}

此代码将打印

sqrt(2) = 1.414213562373095048801688724209698078569671875376948073176679737990732478462107038850387534327641573

序列化

如果您在系统之间传递 BigDecimals,请确保使用支持十进制数且不需要转换为浮点二进制数的序列化格式,否则将会有信息丢失。

如 JSON 这样的文本格式应该可以正常工作,只要接收方也将数字解析为十进制,以保持完整精度准确。通常,JSON 解析实现默认不会这样做,需要特殊配置。

如 msgpack 这样的二进制格式可能期望/要求将数字表示为 64 位 IEEE-754 浮点数,并且默认情况下可能会丢失精度,除非您明确地将十进制格式化为字符串、字节或某种自定义结构。

默认情况下,这将序列化为 字符串

要在此crate中使用serde_json,建议启用serde-json特性(注意serde -json,而不是serde_json),这将添加对将值序列化和反序列化为BigDecimal的支持。默认情况下,它将使用常规约定解析数字和字符串。如果您想将值序列化为数字而不是字符串,可以使用以下示例中所示的serde(with)注解

[dependencies]
bigdecimal = { version = "0.4", features = [ "serde-json" ] }  # '-' not '_'
use bigdecimal::BigDecimal;
use serde::*;
use serde_json;

#[derive(Debug,Serialize,Deserialize)]
struct MyStruct {
    name: String,
    // this will be written to json as string
    value: BigDecimal,
    // this will be written to json as number
    #[serde(with = "bigdecimal::serde::json_num")]
    number: BigDecimal,
}

fn main() {
    let json_src = r#"
        { "name": "foo", "value": 1234567e-3, "number": 3.14159 }
    "#;

    let my_struct: MyStruct = serde_json::from_str(&json_src).unwrap();
    dbg!(my_struct);
    // MyStruct { name: "foo", value: BigDecimal("1234.567"), BigDecimal("3.1459") }

    println!("{}", serde_json::to_string(&my_struct));
    // {"name":"foo","value":"1234.567","number":3.1459}
}

如果您有改进序列化的建议,请带到Zulip聊天中。

格式化

在实现更复杂的格式化解决方案之前(目前正在进行中),我们只能使用Rust的fmt::Display格式化选项。这是此crate格式化BigDecimal的方式

  • {} - 默认显示

    • 格式化为“可读”的数字
    • “小”的分数数(接近零)以科学记数法打印
      • 如果数字的前导零的数量超过阈值,则认为数字“小”
      • 可以通过编译时环境变量配置:RUST_BIGDECIMAL_FMT_EXPONENTIAL_LOWER_THRESHOLD
        • 默认5
      • 示例:1.23e-3将打印为0.00123,但1.23e-10将是1.23E-10
    • 将添加到“小”整数后面的尾随零,以避免使用科学记数法
      • 可能看起来比实际的精度更高
      • 示例:十进制1e1将被渲染为10
      • “小”的阈值由编译时环境变量配置:RUST_BIGDECIMAL_FMT_EXPONENTIAL_UPPER_THRESHOLD
        • 默认15
      • 1e15 => 1000000000000000
      • 大整数(例如1e50000000)将使用科学记数法打印,而不是一个后面跟着五千万个零的1
    • 所有其他数字都使用标准十进制表示法打印
  • {:.<PREC>} - 显示精度

    • 以精确的PREC位数字格式化数字
    • 具有分数部分的数字将在精度点四舍五入,或用零填充到精度点
    • 整数将用零填充到精度点
      • 为了防止不合理的输出大小,一个阈值限制了填充零的数量
        • 比默认情况大,因为请求了特定的精度
        • 可以通过编译时环境变量配置:RUST_BIGDECIMAL_FMT_MAX_INTEGER_PADDING
          • 默认1000
      • 如果数字超过此阈值,它们将不带小数点打印,并附加大数的比例
  • {:e} / {:E} - 指数格式

    • 使用eE作为指数分隔符以科学记数法格式化
    • 精确保持精度
  • {:.<PREC>e} - 以科学记数法格式化,保留数字

    • 数字会被四舍五入或补零,直到
  • {:?} - 调试

    • 显示 BigDecimal 的内部表示
      • 123.456 => BigDecimal(sign=Plus, scale=3, digits=[123456])
      • -1e10000 => BigDecimal(sign=Minus, scale=-10000, digits=[1])
  • {:#?} - 交替调试(由 dbg!() 使用)

    • 显示 BigDecimal 的简单整数+指数字符串表示
      • 123.456 => BigDecimal("123456e-3")
      • -1e10000 => BigDecimal("-1e10000")

examples/ 目录中有一个 formatting-example 脚本,该脚本演示了格式化选项与 Rust 标准浮点数 Display 的比较。

建议你在代码中包含单元测试,以确保 BigDecimal 的未来版本继续以你期望的方式格式化数字。上述规则不太可能更改,但它们可能是此库中相对主观的部分,并且可能会在没有编译器指示的情况下更改行为。

此外,请检查配置环境变量的更改,这些变量可能会在 1.0 之前更改名称。

编译时配置

您可以通过环境变量在 编译时 设置一些默认参数

环境变量 默认
RUST_BIGDECIMAL_DEFAULT_PRECISION 100
RUST_BIGDECIMAL_DEFAULT_ROUNDING_MODE HalfEven
RUST_BIGDECIMAL_FMT_EXPONENTIAL_LOWER_THRESHOLD 5
RUST_BIGDECIMAL_FMT_EXPONENTIAL_UPPER_THRESHOLD 15
RUST_BIGDECIMAL_FMT_MAX_INTEGER_PADDING 1000

这些允许全局设置默认的 Context 字段,而无需进行运行时查找,或需要在所有计算中传递 Context 参数。(如果您想要对这些字段进行运行时控制,您将必须将 Context 传递给您的函数。)查看 build.rs 了解这些如何转换为代码中的常量(如果感兴趣)。

默认精度

默认精度可以通过环境变量 RUST_BIGDECIMAL_DEFAULT_PRECISION 在编译时设置。此变量的默认值为 100。

这将用作可能产生无限位数的操作的最大精度(倒数、平方根等)。

请注意,其他操作(如乘法)将保留所有数字;因此,乘以两个70位数字将得到一个140位数字。用户必须手动使用 x.with_prec(30) 方法来手动剪裁计算后的数字到合理的数量。

正在开发一套具有显式精度和舍入模式的新的方法集,但是即使引入了这些方法,默认精度仍必须用作隐式值。

舍入模式

默认上下文使用此值进行舍入。有效值是 RoundingMode 枚举的变体。

默认为 HalfEven

指数格式阈值

在格式化程序使用指数形式(即科学计数法)之前,小数点后最多可以有几位前导零。

目前没有机制在运行时更改此值。如果您知道Rust中数字格式化的好解决方案,请告诉我!

示例编译时配置

给定程序

fn main() {
    let n = BigDecimal::from(700);
    println!("1/{n} = {}", n.inverse());
}

使用不同的环境变量编译会打印出不同的结果

$ export BIG_DECIMAL_DEFAULT_PRECISION=8
$ cargo run
1/700 = 0.0014285714

$ export RUST_BIGDECIMAL_DEFAULT_PRECISION=5
$ cargo run
1/700 = 0.0014286

$ export RUST_BIGDECIMAL_DEFAULT_ROUNDING_MODE=Down
$ cargo run
1/700 = 0.0014285

$ export RUST_BIGDECIMAL_EXPONENTIAL_FORMAT_THRESHOLD=2
$ cargo run
1/700 = 1.4285E-3

[!注意]

这些是 编译时 环境变量,BigDecimal库默认不支持通过环境变量或任何类型的全局变量在 运行时 配置,除非显式指定。

这是为了灵活性和性能。

关于

此存储库包含原本打算用于流行的 num crate 中的bigdecimal模块的代码,但由于对最佳设计的怀疑而没有合并。

许可证

此代码在宽松的 MIT & Apache 2.0 许可证下双许可。

贡献

除非您明确表示,否则任何有意提交以包含在您的工作中的贡献,根据Apache-2.0许可证定义,均应如上所述双许可,不附加任何额外条款或条件。

依赖关系

~0.8–1.2MB
~26K SLoC