2个版本

0.1.1 2024年1月3日
0.1.0 2024年1月3日

#615 in 数学

MIT许可证

42KB
922

astro_float for nalgebra

此包实现了在astro_float中的类型BigFloat上使用线性代数库nalgebra所需的特性和功能。此库导出了它自己的类型BigFloat<CTX>,其中CTX是一个计算上下文,用于存储精度和舍入模式。

示例

编译时常量精度

使用已知于编译时的精度和舍入模式与该库一起使用是直接的,可以使用ConstCtx计算上下文。建议将BigFloat<ConstCtx<P,RM>>存储在类型别名中,以便代码更易于阅读。

use astro_nalgebra::{BigFloat, ConstCtx, RoundingMode};
use nalgebra::Vector2;

// This defines a type that has a precision upper bound of
// 1024 bits in the mantissa and no explicit rounding mode 
type BF1024 = BigFloat<ConstCtx<1024>>;

// See the documentation on ConstCtx for how to specify a rounding mode

fn main() {
    let two: BF1024 = "2".parse().unwrap();
    let six: BF1024 = "6".parse().unwrap();
    let vec: Vector2<BF1024> = Vector2::new(two,six);
    let seven: BF1024 = "7".parse().unwrap();
    // Prints [2/7, 6/7] as decimals until 1024 bits
    println!("{}", vec / seven);
}

尽管完全可以将类型命名为类似于f1024,但从技术上讲,这打破了浮点命名方案,因为类型BigFloat<ConstCtx<64>>的尾数部分有64位,而像f64这样的类型只有52位尾数,其中有12位保留用于符号和指数。因此,f64BigFloat<ConstCtx<64>>并不相同。

运行时动态精度

动态精度更难实现,因为《nalgebra::RealField》中概述的一些方法没有任何参数,因此精度必须存储在类型中。然而,运行时确定的变量不能存储在const generic中,因此必须有一个具有get_precget_rm方法的无用类型。有一个宏可以快速定义这个无用类型,它引用一个全局的、线程安全的OnceLock,该锁在运行时必须设置。

示例

use astro_nalgebra::{BigFloat, make_dyn_ctx, RoundingMode};

// This macro takes in the name of a dynamic context (the new type to be made)
// And the name of a global OnceLock to store the precision and rounding mode.
// The name of this global variable is not important, it just has to be unique
// within the scope of the macro call.
make_dyn_ctx!(DynCtx, DYN_CTX_CELL);

type DynFloat = BigFloat<DynCtx>;
fn main() {
    let precision = 88;
    let rounding_mode = RoundingMode::None;
    // Sets the precision and rounding mode of the DynCtx context.
    // This method can only be called once or it will panic.
    DynCtx::set(precision, rounding_mode);
    
    let num: DynFloat = "120".parse().unwrap();
}

为什么必须使用泛型来实现

该库可能有两种实现方式

  1. 一个非常简单的包装器,围绕astro_float::BigFloat,将精度存储在结构体本身中。
  2. 一个包装器,将精度作为泛型存储在类型中。

确保正确精度的唯一方法是使用后一种技术。以下是一个示例,说明前一种技术会如何失败

use nalgebra::RealField;
fn mul_by_pi<T: RealField>(val: T) -> T {
    val * T::pi()
}

如果计算所需的精度存储在val中,那么调用BigFloat::pi()将无法访问精度,因为RealField::pi()不接受任何参数。因此,所需的精度必须存储在类型本身中。

依赖项

~6MB
~124K SLoC