3 个版本 (破坏性更新)
0.10.3 | 2024年2月11日 |
---|---|
0.10.1 |
|
0.10.0 |
|
0.8.1 | 2023年6月28日 |
0.2.0 |
|
#89 in 数据结构
每月 1,586 次下载
在 4 crate 中使用
285KB
7K SLoC
此 crate 提供了 Decimal
定点算术的快速实现。它针对典型的商业应用,处理表示数量、货币等数值,而不是针对科学计算,对于后者,浮点数学的精度在大多数情况下是足够的。
目标
- 十进制数的“精确”表示(没有像二进制浮点数那样的偏差)
- 没有隐藏的舍入误差(如浮点数学固有的)
- 非常快速的操作(通过将它们映射到整数操作)
- 可表示的十进制数的范围足以满足典型的商业应用
在二进制级别,Decimal
数表示为一个系数(存储为 i128
值)和一个指定小数位数(存储为 u8
)。后者限制为常量 MAX_N_FRAC_DIGITS
= 18 的值。
状态
正在进行中,但大多数 API 都是稳定的。
入门
将 fpdec
添加到您的 Cargo.toml
[dependencies]
fpdec = "0.10"
用法
Decimal
数可以以不同的方式创建。
最简单的方法是使用过程宏 Dec
# use fpdec::{Dec, Decimal};
let d = Dec!(-17.5);
assert_eq!(d.to_string(), "-17.5");
或者,您可以转换整数、浮点数或字符串为 Decimal
# use fpdec::Decimal;
let d = Decimal::from(297_i32);
assert_eq!(d.to_string(), "297");
# use fpdec::{Decimal, DecimalError};
# use core::convert::TryFrom;
let d = Decimal::try_from(83.25_f64)?;
assert_eq!(d.to_string(), "83.25");
# Ok::<(), DecimalError>(())
# use fpdec::{Decimal, ParseDecimalError};
# use core::str::FromStr;
let d = Decimal::from_str("38.2070")?;
assert_eq!(d.to_string(), "38.2070");
# Ok::<(), ParseDecimalError>(())
可以使用一元减号运算符反转 Decimal
的符号,并且可以比较 Decimal
实例与其他类型的 Decimal
实例或所有基本整数类型(除了 u128)
# use fpdec::{Dec, Decimal};
let x = Dec!(129.24);
let y = -x;
assert_eq!(y.to_string(), "-129.24");
assert!(-129_i64 > y);
let z = -y;
assert_eq!(x, z);
let z = Dec!(0.00097);
assert!(x > z);
assert!(y <= z);
assert!(z != 7_u32);
assert!(7_u32 == Dec!(7.00));
Decimal
支持所有五个二进制数值运算符 +, -, *, / 和 %,可以用于两个 Decimal
之间,或者一个 Decimal
和一个基本整数(除了 u128)之间。
# use fpdec::{Dec, Decimal};
let x = Dec!(17.5);
let y = Dec!(6.40);
let z = x + y;
assert_eq!(z.to_string(), "23.90");
let z = x - y;
assert_eq!(z.to_string(), "11.10");
let z = x * y;
assert_eq!(z.to_string(), "112.000");
let z = x / y;
assert_eq!(z.to_string(), "2.734375");
let z = x % y;
assert_eq!(z.to_string(), "4.70");
# use fpdec::{Dec, Decimal};
let x = Dec!(17.5);
let y = -5_i64;
let z = x + y;
assert_eq!(z.to_string(), "12.5");
let z = x - y;
assert_eq!(z.to_string(), "22.5");
let z = y * x;
assert_eq!(z.to_string(), "-87.5");
let z = x / y;
assert_eq!(z.to_string(), "-3.5");
let z = x % y;
assert_eq!(z.to_string(), "2.5");
乘法或除法的结果在任何情况下都不是精确的。如果精确结果的分数小数位数超过 MAX_N_FRAC_DIGITS
分数小数位数,则结果会四舍五入以适应此限制。
# use fpdec::{Dec, Decimal};
let x = Dec!(1e-10);
let y = Dec!(75e-9);
let z = x * y;
assert_eq!(z.to_string(), "0.000000000000000008");
let x = Dec!(1.);
let y = Dec!(3.);
let z = x / y;
assert_eq!(z.to_string(), "0.333333333333333333");
所有这些二进制数值运算符如果结果不能按照上述约束表示为 Decimal
,则会引发 panic。此外,还有实现“已检查”运算符变体的函数,这些函数在失败时返回 Option::None
而不是 panic。
对于乘法和除法,还有返回四舍五入到指定小数位数的结果的函数。
# use fpdec::{Dec, Decimal, DivRounded, MulRounded};
let x = Dec!(17.5);
let y = Dec!(6.47);
let z: Decimal = x.mul_rounded(y, 1);
assert_eq!(z.to_string(), "113.2");
let z: Decimal = x.div_rounded(y, 3);
assert_eq!(z.to_string(), "2.705");
Decimal
值可以被转换成浮点数,可能四舍五入到目标类型可以表示的最接近的值。
# use fpdec::{Dec, Decimal};
let d = Dec!(-33820900478.195);
let f = f64::from(d);
assert_eq!(f, -33820900478.19499969482421875_f64);
let f = f32::from(Dec!(0.6));
assert_eq!(f, 0.60000002384185791015625_f32);
将 Decimal
值转换为原始 int 类型更为复杂。它只通过 try_from / try_into 支持,并且只有当给定的值代表适合目标类型值范围的整数时,才提供目标类型的值。
# use fpdec::{Dec, Decimal, TryFromDecimalError};
let d = Dec!(3.7);
let res = i32::try_from(d);
assert!(res.is_err());
assert_eq!(res.unwrap_err(), TryFromDecimalError::NotAnIntValue);
let d = Decimal::MAX;
let res = i128::try_from(d);
assert_eq!(res.unwrap(), i128::MAX);
let res = i64::try_from(d);
assert!(res.is_err());
assert_eq!(res.unwrap_err(), TryFromDecimalError::ValueOutOfRange);
仓库特性
默认情况下,仅启用 std
特性。
生态系统
-
std - 当启用时,这将导致
fpdec
使用标准库,因此可以提供字符串转换、格式化和打印功能。当禁用时,需要使用 cratealloc
和特定于系统的分配器来使用这些功能。 -
packed - 当启用时,结构体
Decimal
被标记为#[repr(packed)]
。
可选依赖
-
num-traits - 当启用时,为
Decimal
实现num-traits::Num
特性。 -
serde-as-str - 当启用时,启用对
serde
的支持。这允许Decimal
实例作为字符串序列化,并可以通过serde
从字符串反序列化。 -
rkyv - 当启用时,启用对
rkyv
的支持。这允许Decimal
实例通过rkyv
归档进行零拷贝序列化和反序列化。
依赖项
~18–460KB
~10K SLoC