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数据结构

Download history 149829/week @ 2024-05-03 170723/week @ 2024-05-10 163959/week @ 2024-05-17 186046/week @ 2024-05-24 220271/week @ 2024-05-31 251252/week @ 2024-06-07 218514/week @ 2024-06-14 177869/week @ 2024-06-21 155072/week @ 2024-06-28 237731/week @ 2024-07-05 221836/week @ 2024-07-12 187602/week @ 2024-07-19 195078/week @ 2024-07-26 180587/week @ 2024-08-02 311462/week @ 2024-08-09 226236/week @ 2024-08-16

951,053 每月下载量
用于 254 个crate(46 个直接使用)

MIT/Apache

640KB
15K SLoC

Fraction

无损分数和小数;浮点数的直接替换

GitHub Actions Documentation Current Version on crates.io MIT / Apache2 License

功能

  • 分数类型,表示为分数的浮点数
  • 小数类型,基于分数类型,具有显式精度
  • 分数可以作为浮点数的直接替换,除了 NaN == NaN && NaN < NegInfinity,因此它是可散列和可排序的。
  • 分数是可散列和可排序的,因此可以在集合、HashMap 和 BTree 中使用。
  • DynaInt 类型,它是一个动态增长到堆的 int。执行检查性数学运算,避免栈溢出。
  • 为 Numeric/Decimal 类型提供 PostgreSQL 集成(无需额外内存分配)
  • Juniper 集成分数和小数
  • 支持泛型整数转换,例如 i8 -> u8usize -> u8
  • 具有无限精度的无损除法,且无需内存分配

文档

这里: Documentation

示例

简单使用

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());
}

转换为/从其他类型转换

分数和小数类型都实现了

  • fromtry_into 对于所有内置原始类型。
  • fromtry_into 对于 BigIntBigUint 当启用 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");

泛型整数构造函数(使用松散的数/分母类型构造)

如果您有两个不兼容类型的 numeratordenominator,它们不能隐式转换为单个公共类型。例如。

  • 分子 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。但是,也可以通过 DynaIntsomething_more_than_u8> 来规避。

建议使用 DynaInt<usize, BigUint> 以便默认情况下有栈上数学运算,并在必要时分配堆内存。

否则,这两种类型(分数和小数)应按照 Postgres crate 文档透明地工作。

变更日志

有关详细信息,请参阅 CHANGELOG.md 文件

依赖项

~0.4–2.4MB
~52K SLoC