2 个版本
0.1.1 | 2023 年 9 月 13 日 |
---|---|
0.1.0 | 2023 年 9 月 10 日 |
#1056 in Rust 模式
2,459 每月下载量
28KB
424 行
这是 IEEE APSqRt,一个用于与 rustc_apfloat 的 IEEE 754 浮点数一起使用的平方根实现。
(APFloat 是任意精度浮点数的缩写。IEEE APSqRt 是 IEEE 754 APFloat Square Root 的缩写。与 APFloat 不同,它只设计用于与 IEEE 754 单精度、双精度和四精度浮点数一起工作;因此,它本身不提供任意精度。)
原因
rustc_apfloat 是一个软件浮点数库。它是 LLVM 项目的 "APFloat" 的 Rust 版本。这些库强调清晰性和正确性,但速度仍然相当快。它们分别用于 rustc
和 clang
编译器,以进行编译时的浮点数运算。这些编译器不使用宿主机的浮点数,因为在交叉编译时,宿主机和目标机的行为可能很难或不可能协调。仿真器通常也有相同的问题。我在编写一个确定性的 RV32-G 仿真器,作为其中的一部分,我需要特别模拟 RISC-V 标准的浮点数规范。rustc_apfloat 来拯救!
因为 rustc_apfloat 实现了 RISC-V 浮点数仿真所需的几乎所有操作,这使我的生活变得更加容易——但是最后一个操作,平方根,非常重要,所以我必须自己实现它。使用宿主机的 sqrt
函数来做这些会更快、更简单,并且接受仿真的不一致性,但我的仿真器旨在适应多人游戏逻辑循环(?!?!?!),所以它必须是确定的。一个在任何平台上都是完全相同的错误的差劣算法,因此比一个即使有微小的平台之间差异的优良算法更好。
我最初实现了一个非常简单的猜测和检查算法(见 Bad APSqRt),但是一旦我完成了这个,我就有了一切需要来实现和理解牛顿-拉夫森的方法。这个包是这个结果。
算法
IEEE APSqRt实现了牛顿-拉夫森方法,也称为巴比伦方法。它首先形成初始猜测值 2^(指数/2)
,然后通过以下公式反复优化它: new_guess = (old_guess + square / old_guess) / 2
。经过5-7次迭代后,这个值将非常接近答案。
精度
如果有精确解,IEEE APSqRt的任何形式都能找到它。对于sqrt_fast
,大约70%的不精确解将是正确的,大约30%的解将偏离一个ulp,而极少数解将偏离两个ulp。对于执行更高精度操作的sqrt_slow
,通常只需要再迭代一次,答案将100%正确。(遗憾的是,由于rustc_apfloat的外部接口限制,sqrt_slow
仅适用于32位和64位浮点数。)
IEEE APSqRt产生的所有NaN都是符合RISC-V标准的“规范NaN”。
如何使用
使用ieee_apsqrt::sqrt_fast
调用一个u32
/u64
/u128
,或者使用ieee_apsqrt::sqrt_accurate
调用一个u32
/u64
。前者函数精度稍低,但大约快两倍。后者函数尽可能精确,但大约慢一半。这两个函数还要求一个舍入模式。
这两个函数都返回一个元组,为(rustc_apfloat::StatusAnd<uXX>, u32)
,其中第一个值是计算结果,第二个值是需要的牛顿-拉夫森迭代次数。
如果您在仿真用途中计算时钟周期,请将平方根的基本成本视为一次乘法,迭代成本为一次除法、一次加法和一次乘法。将sqrt_fast
视为在请求的精度下执行操作,而sqrt_accurate
在两倍精度下执行(例如,单精度变为双精度,双精度变为四精度)。
法律声明
IEEE APSqRt版权所有 2023 Solra Bizna。
IEEE APSqRt采用Apache 2许可证,并带有LLVM例外条款。这是与rustc_apfloat crate相同的许可证。这是确保IEEE APSqRt可以在rustc_apfloat可以使用的地方使用的最简单方法。
依赖关系
~355KB