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 在 数据结构 中
187,372 每月下载量
在 199 个 crate 中使用(127 直接使用)
400KB
7.5K SLoC
Decimal
一个纯 Rust 编写的 Decimal 数字实现,适用于需要大量整数和小数位且无舍入误差的金融计算。
二进制表示由一个 96 位整数、一个用于指定小数分数的缩放因子和一个 1 位符号组成。由于这种表示,尾随零被保留,并且在字符串形式下可能会暴露出来。这些可以通过 normalize
或 round_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
- serde-float
- serde-str
- serde-arbitrary-precision
- serde-with-float
- serde-with-str
- serde-with-arbitrary-precision
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
功能可以启用额外的复杂数学函数,如 pow
、ln
、enf
、exp
等。有关附加函数的详细说明,请参阅 MathematicalOps
特性。
请注意,ln
和 log10
在无效输入时会引发恐慌。使用 checked_ln
和 checked_log10
是避免这种情况的首选函数。当 maths
功能最初开发时,库会在无效输入时返回 0
。要重新启用这种非恐慌行为,请使用功能:maths-nopanic
。
ndarray
启用使用 ndarray
在 Decimal
数组上执行算术操作。
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-*
。
这对于类似 bincode
或 csv
的实现通常很有用。
由于 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