59 个稳定版本

新增 1.36.0 2024 年 8 月 19 日
1.35.0 2024 年 7 月 11 日
1.34.2 2024 年 2 月 1 日
1.33.1 2023 年 11 月 16 日
0.6.1 2017 年 11 月 11 日

#212数据结构

Download history 38138/week @ 2024-05-02 41685/week @ 2024-05-09 40506/week @ 2024-05-16 39878/week @ 2024-05-23 40230/week @ 2024-05-30 38717/week @ 2024-06-06 38259/week @ 2024-06-13 37841/week @ 2024-06-20 35822/week @ 2024-06-27 37979/week @ 2024-07-04 40113/week @ 2024-07-11 38858/week @ 2024-07-18 42705/week @ 2024-07-25 42643/week @ 2024-08-01 49689/week @ 2024-08-08 44038/week @ 2024-08-15

187,372 每月下载量
199 crate 中使用(127 直接使用)

MIT 许可证

400KB
7.5K SLoC

Decimal 构建状态 最新版本 文档徽章

一个纯 Rust 编写的 Decimal 数字实现,适用于需要大量整数和小数位且无舍入误差的金融计算。

二进制表示由一个 96 位整数、一个用于指定小数分数的缩放因子和一个 1 位符号组成。由于这种表示,尾随零被保留,并且在字符串形式下可能会暴露出来。这些可以通过 normalizeround_dp 函数进行截断。

安装

$ cargo add rust_decimal

此外,如果您想使用优化后的宏以便于创建十进制数

$ cargo add rust_decimal_macros

或者,您可以直接编辑您的 Cargo.toml 并运行 cargo update

[dependencies]
rust_decimal = "1.36"
rust_decimal_macros = "1.36"

用法

Decimal 数字可以通过几种不同的方式创建。创建 Decimal 最简单和最有效的方法是使用可以通过 macros 功能启用的过程宏

// Import the `rust_decimal_macros` crate and use the macro directly from there.
use rust_decimal_macros::dec;

let number = dec!(-1.23) + dec!(3.45);
assert_eq!(number, dec!(2.22));
assert_eq!(number.to_string(), "2.22");

或者您也可以使用 Decimal 数字便捷函数之一(请参阅文档以获取更多详细信息

// Using the prelude can help importing trait based functions (e.g. core::str::FromStr).
use rust_decimal::prelude::*;

// Using an integer followed by the decimal points
let scaled = Decimal::new(202, 2);
assert_eq!("2.02", scaled.to_string());

// From a 128 bit integer
let balance = Decimal::from_i128_with_scale(5_897_932_384_626_433_832, 2);
assert_eq!("58979323846264338.32", balance.to_string());

// From a string representation
let from_string = Decimal::from_str("2.02").unwrap();
assert_eq!("2.02", from_string.to_string());

// From a string representation in a different base
let from_string_base16 = Decimal::from_str_radix("ffff", 16).unwrap();
assert_eq!("65535", from_string_base16.to_string());

// From scientific notation
let sci = Decimal::from_scientific("9.7e-7").unwrap();
assert_eq!("0.00000097", sci.to_string());

// Using the `Into` trait
let my_int: Decimal = 3_i32.into();
assert_eq!("3", my_int.to_string());

// Using the raw decimal representation
let pi = Decimal::from_parts(1_102_470_952, 185_874_565, 1_703_060_790, false, 28);
assert_eq!("3.1415926535897932384626433832", pi.to_string());

一旦实例化了您的 Decimal 数值,就可以像任何其他数值一样对其进行计算。

use rust_decimal::prelude::*;
use rust_decimal_macros::dec;

let amount = dec!(25.12);
let tax_percentage = dec!(0.085);
let total = amount + (amount * tax_percentage).round_dp(2);
assert_eq!(total, dec!(27.26));

功能

行为/功能

数据库

Serde

borsh

启用 Borsh 序列化功能,适用于 Decimal

c-repr

强制 Decimal 使用 [repr(C)]。相应的目标布局是128位对齐。

db-postgres

启用 PostgreSQL 通信模块。允许通过透明地将 Decimal 类型序列化和反序列化为 PostgreSQL 中的 NUMERIC 数据类型来读取和写入。

db-tokio-postgres

启用 tokio postgres 模块,允许异步与 PostgreSQL 通信。

db-diesel-postgres

启用 diesel PostgreSQL 支持。

db-diesel-mysql

启用 diesel MySQL 支持。

legacy-ops

警告:这已被弃用,并将从未来的版本中删除。

1.10 版本开始,执行基本操作所使用的算法已更改,这带来了显著的性能提升。为了保持向后兼容性,可以通过启用 legacy-ops 功能来选择退出。

maths

启用 maths 功能可以启用额外的复杂数学函数,如 powlnenfexp 等。有关附加函数的详细说明,请参阅 MathematicalOps 特性。

请注意,lnlog10 在无效输入时会引发恐慌。使用 checked_lnchecked_log10 是避免这种情况的首选函数。当 maths 功能最初开发时,库会在无效输入时返回 0。要重新启用这种非恐慌行为,请使用功能:maths-nopanic

ndarray

启用使用 ndarrayDecimal 数组上执行算术操作。

proptest

启用 proptest 策略,用于生成 Rust Decimal 的值。

rand

实现 rand::distributions::Distribution<Decimal>,允许创建随机实例。

注意:当使用 rand::Rng 特性生成两个其他十进制数之间的十进制数时,随机生成的十进制数的刻度将与输入十进制数的刻度相同(或者,如果输入具有不同的刻度,则为较高的一个)。

rkyv

启用 rkyv 序列化功能,适用于 Decimal。当启用 rkyv-safe 功能时,也支持 rkyv 的安全 API。

rocket-traits

通过实现 FromFormField 特性来启用对 Rocket 表单的支持。

rust-fuzz

通过实现 Arbitrary 特性来启用 rust-fuzz 支持。

serde-float

注意: 此功能将浮点序列化/反序列化规则作为处理 Decimal 数字的默认方法。有关更灵活的功能,请参阅 serde-with-*

启用此功能,以便将 Decimal 类型的 JSON 序列化发送为浮点数而不是字符串(默认)。

例如,启用此功能后,JSON 序列化将输出

{
  "value": 1.234
}

serde-str

注意: 此功能将字符串序列化/反序列化规则作为处理 Decimal 数字的默认方法。有关更灵活的功能,请参阅 serde-with-*

这对于类似 bincodecsv 的实现通常很有用。

由于 bincode 未指定类型信息,我们需要确保提供类型提示,以便正确地反序列化。单独启用此功能将强制使用 deserialize_str 而不是 deserialize_any 进行反序列化。

如果您还启用了 serde-float,则此功能将使用 deserialize_f64 作为类型提示。因为将 f64 转换为会 丢失 精度,所以在与 bincode 一起使用时,强烈建议您不要启用此功能。话虽如此,这将仅使用 8 字节,因此在存储大小方面略微更有效率。

serde-arbitrary-precision

注意: 此功能将任意序列化/反序列化规则作为处理 Decimal 数字的默认方法。有关更灵活的功能,请参阅 serde-with-*

此功能主要用于与 serde_json 一起使用,因此将其作为“弱依赖项”添加。此功能支持在解析小数时 serde_json 内部的 arbitrary_precision 功能。

在解析看起来像“浮点数”的数据时推荐使用此功能,因为它可以防止数据丢失。

请注意,此功能当前默认以浮点数格式序列化数字,这可能会产生意外的后果。要更好地控制序列化格式,请使用 serde-with-arbitrary-precision 功能。

serde-with-float

启用此功能以访问将 Decimal 类型序列化为浮点数的模块。这可以在如下所示的 struct 定义中使用

#[derive(Serialize, Deserialize)]
pub struct FloatExample {
    #[serde(with = "rust_decimal::serde::float")]
    value: Decimal,
}
#[derive(Serialize, Deserialize)]
pub struct OptionFloatExample {
    #[serde(with = "rust_decimal::serde::float_option")]
    value: Option<Decimal>,
}

或者,如果只想使用序列化功能(例如,在保持反序列化灵活性的同时)

#[derive(Serialize, Deserialize)]
pub struct FloatExample {
    #[serde(serialize_with = "rust_decimal::serde::float::serialize")]
    value: Decimal,
}

serde-with-str

启用此功能以访问将 Decimal 类型序列化为 String 的模块。这可以在如下所示的 struct 定义中使用

#[derive(Serialize, Deserialize)]
pub struct StrExample {
    #[serde(with = "rust_decimal::serde::str")]
    value: Decimal,
}
#[derive(Serialize, Deserialize)]
pub struct OptionStrExample {
    #[serde(with = "rust_decimal::serde::str_option")]
    value: Option<Decimal>,
}

虽然此功能通常不需要用于序列化,但对于反序列化来说可能很有用,因为它不需要类型提示。因此,您可以通过以下方式仅强制反序列化使用此功能

#[derive(Serialize, Deserialize)]
pub struct StrExample {
    #[serde(deserialize_with = "rust_decimal::serde::str::deserialize")]
    value: Decimal,
}

serde-with-arbitrary-precision

启用此功能以访问使用 serde_json/arbitrary_precision 功能反序列化 Decimal 类型的模块。这可以在如下所示的 struct 定义中使用

#[derive(Serialize, Deserialize)]
pub struct ArbitraryExample {
    #[serde(with = "rust_decimal::serde::arbitrary_precision")]
    value: Decimal,
}
#[derive(Serialize, Deserialize)]
pub struct OptionArbitraryExample {
    #[serde(with = "rust_decimal::serde::arbitrary_precision_option")]
    value: Option<Decimal>,
}

此功能的意外后果是它将以类似浮点数的格式进行序列化。为了防止这种情况,您可以将结构体仅针对 arbitrary_precision 功能进行反序列化

#[derive(Serialize, Deserialize)]
pub struct ArbitraryExample {
    #[serde(deserialize_with = "rust_decimal::serde::arbitrary_precision::deserialize")]
    value: Decimal,
}

这将确保序列化仍然以字符串形式发生。

请参阅 examples 目录以获取有关 serde_json 场景的更多信息。

std

启用 std 库支持。这默认启用,但在将来将是可选的。目前,为了支持 no_std 库,可以使用 --no-default-features 编译此包。

构建

有关构建和测试 Rust Decimal 的更多信息,请参阅构建文档

最小 Rust 编译器版本

当前的 最小 编译器版本是 1.60.0,该版本于 2022-04-07 发布。

此库维护对与当前稳定版 Rust 编译器版本相差 4 个小版本的 rust 编译器版本的支持。例如,如果当前稳定编译器版本是 1.50.0,则我们将保证支持到 1.46.0 及更高版本。值得注意的是,只有在需要时,我们才会更新最低支持版本。

与其他 Decimal 实现的比较

在开发此库的过程中,为了确保十进制计算快速、准确和高效,做出了各种设计决策。然而,一些决策限制了此库所能做的事情以及其适用性。其中一个决策是内部十进制表示的结构。

此库使用由三个 32 位无符号整数组成,总共 96 位尾数,以及一个用于表示比例/符号(类似于 C 和 .NET Decimal 实现)的第四个 32 位无符号整数。这种结构使我们能够利用算法优化来实现基本的算术运算;最终,这使我们能够挤出性能,使其成为最快的实现之一。然而,这种方法的一个缺点是,可以表示的最大有效数字大约是 28 个十进制数字(在某些情况下为 29 个)。

虽然这个限制对许多应用程序(例如处理金钱时)来说不是问题,但有些应用程序可能需要表示更多有效数字。幸运的是,有一些替代实现值得调查,例如

如果您对此库是否适合您的项目有进一步的问题,那么请随意开始一个讨论或打开一个问题,我们将尽力帮助您。

依赖关系

~175–300KB