35 个版本
使用旧的 Rust 2015
0.15.3 | 2024 年 5 月 12 日 |
---|---|
0.15.1 | 2024 年 2 月 11 日 |
0.14.0 | 2023 年 9 月 27 日 |
0.13.1 | 2023 年 2 月 24 日 |
0.1.0 | 2016 年 1 月 24 日 |
#29 在 数据结构
951,053 每月下载量
用于 254 个crate(46 个直接使用)
640KB
15K SLoC
Fraction
无损分数和小数;浮点数的直接替换
功能
- 分数类型,表示为分数的浮点数
- 小数类型,基于分数类型,具有显式精度
- 分数可以作为浮点数的直接替换,除了 NaN == NaN && NaN < NegInfinity,因此它是可散列和可排序的。
- 分数是可散列和可排序的,因此可以在集合、HashMap 和 BTree 中使用。
- DynaInt 类型,它是一个动态增长到堆的 int。执行检查性数学运算,避免栈溢出。
- 为 Numeric/Decimal 类型提供 PostgreSQL 集成(无需额外内存分配)
- Juniper 集成分数和小数
- 支持泛型整数转换,例如
i8 -> u8
,usize -> u8
等 - 具有无限精度的无损除法,且无需内存分配
文档
示例
简单使用
type F = fraction::Fraction; // choose the type accordingly with your needs (see prelude module docs)
let two = F::from(0) + 2; // 0 + 2 = 2
let two_third = two / 3; // 2/3 = 0.666666[...]
assert_eq!(F::from(2), two);
assert_eq!(F::new(2u64, 3u64), two_third);
assert_eq!("2/3", format!("{}", two_third)); // print as Fraction (by default)
assert_eq!("0.6666", format!("{:.4}", two_third)); // format as decimal and print up to 4 digits after floating point
小数作为分数的表示层实现。因此,它也是无损的,可能需要显式控制“精度”以进行比较和格式化操作。
type D = fraction::Decimal; // choose the type accordingly with your needs (see prelude module docs)
let result = D::from(0.5) / 0.3;
assert_eq!(format!("{}", result), "1.6"); // calculation result uses precision of the operands
assert_eq!(format!("{:.4}", result), "1.6666"); // explicitly passing precision to format
assert_eq!("1.6666", format!("{}", result.set_precision(4))); // the other way to set precision explicitly on Decimal
构造
Fraction
use std::str::FromStr;
use fraction::{Fraction, Sign};
// fraction crate also re-exports num::{One, Zero} traits for convenience.
use fraction::{One, Zero};
// There are several ways to construct a fraction, depending on your use case
// `new` - construct with numerator/denominator and normalize the fraction.
// "Normalization" means it will always find the least common denominator
// and convert the input accordingly.
let f = Fraction::new(1u8, 2u8);
// `new_generic` - construct with numerator/denominator of different integer types
assert_eq!(f, Fraction::new_generic(Sign::Plus, 1i32, 2u8).unwrap());
// `from` - converts from primitive types such as i32 and f32.
assert_eq!(f, Fraction::from(0.5)); // convert from float (f32, f64)
// `from_str` - tries parse a string fraction. Supports the usual decimal notation.
assert_eq!(f, Fraction::from_str("0.5").unwrap()); // parse a string
// `from_str` - also supports _fraction_ notation such as "numerator/denominator" delimited by slash (`/`).
assert_eq!(f, Fraction::from_str("1/2").unwrap()); // parse a string
// `new_raw` - construct with numerator/denominator but do not normalize the fraction.
// This is the most performant constructor, but does not calculate the common denominator,
// so may lead to unexpected results in following calculations if the fraction is not normalised.
// WARNING: Only use if you are sure numerator/denominator are already normalized.
assert_eq!(f, Fraction::new_raw(1u64, 2u64));
// `one` - implements num::One trait
assert_eq!(f * 2, Fraction::one());
// `zero` - implements num::Zero trait
assert_eq!(f - f, Fraction::zero());
小数
use std::str::FromStr;
use fraction::{Decimal, Fraction};
fn main() {
// There are similar ways to construct Decimal. Underneath it is always represented as Fraction.
// When constructed, Decimal preserves its precision (number of digits after floating point).
// When two decimals are calculated, the result takes the biggest precision of both.
// The precision is used for visual representation (formatting and printing) and for comparison of two decimals.
// Precision is NOT used in any calculations. All calculations are lossless and implemented through Fraction.
// To override the precision use Decimal::set_precision.
let d = Decimal::from(1); // from integer, precision = 0
assert_eq!(d, Decimal::from_fraction(Fraction::from(1))); // from fraction, precision is calculated from fraction
let d = Decimal::from(1.3); // from float (f32, f64)
assert_eq!(d, Decimal::from_str("1.3").unwrap());
let d = Decimal::from(0.5); // from float (f32, f64)
assert_eq!(d, Decimal::from_str("1/2").unwrap());
}
转换为/从其他类型转换
分数和小数类型都实现了
from
和try_into
对于所有内置原始类型。from
和try_into
对于BigInt
和BigUint
当启用with-bigint
功能时。
use fraction::{Fraction, One, BigInt, BigUint};
use std::convert::TryInto;
// Convert from examples (from primitives always succeed)
assert_eq!(Fraction::from(1i8), Fraction::one());
assert_eq!(Fraction::from(1u8), Fraction::one());
assert_eq!(Fraction::from(BigInt::one()), Fraction::one());
assert_eq!(Fraction::from(BigUint::one()), Fraction::one());
assert_eq!(Fraction::from(1f32), Fraction::one());
assert_eq!(Fraction::from(1f64), Fraction::one());
// Convert into examples (try_into returns Result<T, ()>)
assert_eq!(Ok(1i8), Fraction::one().try_into());
assert_eq!(Ok(1u8), Fraction::one().try_into());
assert_eq!(Ok(BigInt::one()), Fraction::one().try_into());
assert_eq!(Ok(BigUint::one()), Fraction::one().try_into());
assert_eq!(Ok(1f32), Fraction::one().try_into());
assert_eq!(Ok(1f64), Fraction::one().try_into());
格式化(转换为字符串)
格式化对 Decimal 和 Fraction 都适用(Decimal 在内部使用 Fraction)。格式化实现紧密遵循 rust Format trait 文档。
type F = fraction::Fraction;
let result = F::from(0.7) / 0.4;
assert_eq!(format!("{}", result), "7/4");
assert_eq!(format!("{:.2}", result), "1.75");
assert_eq!(format!("{:#.3}", result), "1.750");
泛型整数构造函数(使用松散的数/分母类型构造)
如果您有两个不兼容类型的 numerator
和 denominator
,它们不能隐式转换为单个公共类型。例如。
- 分子
i32
- 分母
u32
use fraction::{Sign, GenericFraction};
type F = GenericFraction<u32>;
let fra = F::new_generic(Sign::Plus, 1i8, 42usize).unwrap();
assert_eq!(fra, F::new(1u32, 42u32));
Postgres 使用说明
建议在 PostgreSQL 交互中使用 Decimal 而不是 Fraction。当与 PostgreSQL 交互时,分数类型最多可以保留到浮点数后的 16383 位精度。这可能会导致像 1/3 或 1/7 这样的值性能不佳。Decimal 有自己的显式精度,因此不会意外地计算成千上万的位数。
PostgreSQL 使用 i16 作为其二进制协议,因此您必须将 GenericFraction/GenericDecimal 的基本类型至少设置为 u16。但是,也可以通过 DynaInt
建议使用 DynaInt<usize, BigUint>
以便默认情况下有栈上数学运算,并在必要时分配堆内存。
否则,这两种类型(分数和小数)应按照 Postgres crate 文档透明地工作。
变更日志
有关详细信息,请参阅 CHANGELOG.md 文件
依赖项
~0.4–2.4MB
~52K SLoC