#decimal-number #decimal #numbers #precision #fixed

无std rust_decimal

使用纯Rust编写的十进制数实现,适用于金融和固定精度计算

96个版本 (61个稳定版)

1.36.0 2024年8月19日
1.35.0 2024年3月27日
1.34.3 2024年2月10日
1.33.1 2023年11月16日
0.1.0 2016年11月16日

数学 中排名 2

Download history 243778/week @ 2024-05-03 266376/week @ 2024-05-10 255660/week @ 2024-05-17 248942/week @ 2024-05-24 271802/week @ 2024-05-31 265612/week @ 2024-06-07 257672/week @ 2024-06-14 260048/week @ 2024-06-21 253712/week @ 2024-06-28 278203/week @ 2024-07-05 252312/week @ 2024-07-12 262885/week @ 2024-07-19 282566/week @ 2024-07-26 267021/week @ 2024-08-02 282489/week @ 2024-08-09 290619/week @ 2024-08-16

每月下载量 1,174,301
1,291 个crate中使用(576个直接使用)

MIT 许可证

385KB
8K SLoC

十进制 构建状态 最新版本 文档徽章

使用纯Rust编写的十进制数实现,适用于需要进行大量整数和小数位计算且无舍入误差的金融计算。

二进制表示由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"

用法

可以通过几种不同的方式创建十进制数。创建十进制数最简单和最有效的方法是使用可以使用 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");

另外,您也可以使用十进制数便捷函数之一(更多信息请参阅文档

// 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

启用对 DecimalBorsh 序列化。

c-repr

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

db-postgres

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

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

启用对 Decimalrkyv 序列化。当启用 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,
}

这将确保序列化仍然以字符串的形式进行。

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

std

启用 std 库支持。默认情况下,这将启用,但将来将需要选择启用。目前,为了支持 no_std 库,可以带 --no-default-features 选项编译此 crate。

构建

有关构建和测试 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 位)。

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

如果您对将此库用于您的项目的适用性有任何疑问,请随时开始讨论或打开一个问题,我们将尽力帮助。

依赖项

~0.3–34MB
~542K SLoC