#rational-numbers #range #fixed #valid #fix #rat #denominator

nightly fix-rat

具有固定分母的有理数类型

2 个版本

0.1.1 2021年2月3日
0.1.0 2021年1月23日

#1647 in 算法

Apache-2.0

24KB
229

修复(了) Rat(ional)

fix-rat 是一个在编译时选择分母的有理数。

它有一个固定的有效范围。

# use fix_rat::Rational;
type R = Rational<{ i64::MAX / 64}>;

let a: R = 60.into();
let b: R = 5.0.into();
let c = a.wrapping_add(b);

let c = c.to_i64();
assert_eq!(c, -63);

预期用途

它打算在以下情况下替换常规浮点数(f64/f32)

  1. 需要可靠的精度,例如处理货币。如果您有“小数点后 x 位十进制/二进制”这样的要求,那么这个包可能适合您。

  2. 需要“确定性”(实际上是交换和结合)的多线程行为。如果您想对值应用更新,并且结果应该不取决于更新应用的顺序,那么这个包可能适合您。

  3. 如果已知可能的值范围,则精度更高。浮点数是多标量,可以表示极小和极大的数。如果您的计算保持在已知区间内,例如 [-1, 1],则 fixed-rat 可能能提供更好的精度和性能。

  4. 待办事项:低精度和高性能要求:浮点数只有两种变体,f32 和 f64,分别使用 32 和 64 位。如果您不需要那么多精度,如果 16 位甚至 8 位对于您的用例足够,您可以在相同的空间中存储更多的数字。由于较低的内存带宽和 SIMD 指令的可用性,这可能导致受影响的代码片段的速度提高近 2 倍或 4 倍。

注意事项(技巧和窍门)

对于可靠的精度:请记住,您在每次操作中仍然会丢失精度,除了使用大整数之外,没有其他方法可以避免这一点。

与浮点数不同,有理数有一个有效范围,并且很容易溢出或下溢。可能建议选择略微更大的可表示范围。

使用有理数不会自动使多线程代码具有确定性。浮点数的确定性丢失发生在计算改变数字的刻度(指数)时。有理数始终在相同的刻度上,但现在您必须处理范围溢出。

最简单的行为是使用 wrapping_op。它总是成功,并且可以以任何顺序使用任何值执行,而不会丢失确定性。但这可能不适合您的用例。

第二种最简单的方法是使用checked_op结合unwrap。如果你的基本值只以较小的增量改变,这可能没问题。选择一个稍大的可表示范围,并在同步代码中处理溢出,例如通过限制在有效范围内(小于可表示范围)。

你不能,至少不能天真地检查checked_op,因为这通常会导致不同执行顺序的行为差异。正确地做到这一点是最困难的选择,但可能是必须的。

使用饱和操作符(saturating_op)可能是完全有效的,但你需要小心确保值只能朝一个方向推进(要么是向最大值推进,要么是向最小值推进)。否则,不同的执行顺序会导致不同的结果。提醒一下,添加负值也是减法操作!

假设范围是[-10,10]:9 + 2 = 10, 10 - 1 = 99 - 1 = 8, 8 + 2 = 10 9 != 10。

在不同的刻度间移动,主要是通过乘除,可能比您习惯的浮点数要损失更多的精度。例如,除以2在浮点数中不会损失精度,它只是减少指数的一位。在有理数中,这会损失一位精度。记住,虽然有理数最初有63位,但f64只有53位。

实现

这是一个围绕整数的一个超级简单的包装器,基本上所有操作都是直接通过的。所以两个有理数的加法实际上只是一个简单的整数加法。

将整数/浮点数转换为有理数只需将其乘以所选的DENOM,将有理数转换为整数/浮点数则除以。

代码非常简单。这个crate的主要价值是技巧和易用性。

待办事项/笔记

目前对整数的泛化有点……令人烦恼。同时对整数进行泛化,同时取该类型的一个值作为const泛型,目前无法进行类型检查。因此,支持用例4可能需要一些宏(i & u 8、16、32、64)。目前它始终是i64。

我可能需要提供一些原子操作以提供便利,至少是加法和减法。尽管它们只是在转换后的原子上进行简单的添加/减法。

目前没有不同分母的有理数之间的交互。这可以改进,但可能需要等待更好的const泛型。

夜间版本

这个crate非常依赖于const泛型(min_const_generics)。

贡献

请参阅存储库根目录中的README.md。

依赖项

~94–325KB