任意精度分数算术(C/C++)中的浮点数与有理数

Floats vs rationals in arbitrary precision fractional arithmetic (C/C++)

本文关键字:C++ 浮点数 有理数 精度 任意      更新时间:2023-10-16

由于有两种实现AP分数的方法,一种是模拟double数据类型的存储和行为,只使用更多的字节,另一种是使用现有的整数APA实现将分数表示为有理数,即表示为一对整数、分子和分母,就性能而言,这两种方法中哪一种更有可能提供高效的算术?(内存使用确实是个小问题。)

我知道现有的C/C++库,其中一些库提供带有"浮点"的分数APA,另一些库提供有理数(然而,它们都没有定点APA)。当然,我可以将依赖于"浮点"实现的库与使用合理实现的库进行比较,但结果在很大程度上取决于那些特定库的实现细节,我必须从近十个可用库中随机选择。因此,我感兴趣的两种方法(如果考虑到定点APA,则为三种)的更多理论利弊。

问题是您在标题中提到的任意精度是什么意思。它的意思是"任意的,但在编译时预先确定,在运行时固定"吗?或者它的意思是"无限的,即在运行时可扩展以表示任何有理数"?

在前一种情况下(在编译时可以自定义精度,但稍后会修复),我认为最有效的解决方案之一实际上是定点算术(即,您提到的两种都没有)。

首先,定点算术不需要任何用于基本算术运算的专用库。它只是一个覆盖在整数算术之上的概念。这意味着,如果你真的需要在点后有很多数字,你可以取任何一个大整数库,把所有数据乘以2^64,你基本上立即得到点后有64个二进制数字的定点算术(至少就算术运算而言,需要对乘法和除法进行一些额外的调整)。这通常比浮点或有理表示更有效。

还要注意的是,在许多实际应用中,乘法运算通常伴随着相互"补偿"的除法运算(如x = y * a / b),这意味着通常没有必要对这种乘法和除法进行任何调整。这也有助于提高定点运算的效率。

其次,定点运算在整个范围内提供统一的精度。浮点表示或有理表示都不是这样,在某些应用程序中,这可能是后两种方法的一个显著缺点(或好处,取决于您需要什么)。

那么,为什么只考虑浮点和有理表示呢。是否有什么东西阻止您考虑定点表示?

由于似乎没有其他人提到这一点,有理数和浮点数代表不同的数字集。值1/3可以用有理数精确表示,但不能用浮点表示。即使是任意精度的浮点,也需要无限多的尾数位来表示像1/3这样的重复小数。这是因为浮点实际上像有理数,但分母被限制为2的幂。任意精度有理数可以表示任意精度浮点数所能表示的一切,甚至更多,因为分母可以是任何整数,而不是2的幂。(也就是说,除非我严重误解了任意精度浮点的实现方式。)

这是对你提出的理论利弊的回应。

我知道你没有问记忆使用情况,但这里有一个理论比较,以防其他人感兴趣。如上所述,有理数专门用于可以用分数表示法简单表示的数字,如1/3492113/203233,而浮点数专门用于用2次方的科学表示法简单表达的数字,例如5*2^4591537*2^203233。以人类可读的形式表示数字所需的ascii类型的数量与它们的内存使用量成比例。

如果我有任何错误,请在评论中纠正我。

无论哪种方式,都需要对任意大小的整数进行乘法运算。这将是您性能的主要因素,因为它的复杂性比O(n*log(n))更差。像对齐操作数和加减大整数这样的操作是O(n),所以我们将忽略这些操作。

对于简单的加法和减法,浮点*不需要乘法运算,有理数不需要3次乘法运算。浮子轻而易举地获胜。

对于乘法,浮点数需要一次乘法运算,有理数需要两次乘法运算。浮子有优势。

分工有点复杂,理性可能会在这里获胜,但这绝非必然。我认为这是一场平局。

因此,总的来说,IMHO,加法对于理性至少是O(n*log(n)),对于浮点至少是O(n),这一事实清楚地为浮点表示带来了胜利。

*如果你的指数基数和数字基数不同,你可能需要一次乘法来执行加法。否则,如果使用2的幂作为基数,则对齐操作数需要进行位移位。如果你不使用二的幂,那么你可能还必须乘以一个数字,这也是O(n)运算。

你实际上在问一个问题:"我需要和我选择的动物一起参加比赛。我应该选择乌龟还是蜗牛?"。

第一个建议"模拟双精度"听起来像是交错精度:使用一个双精度数组,其和是定义的数字。Douglas M.Priest的一篇论文《任意精度浮点运算的算法》描述了该算法的实现方法。我实现了这一点,我的经验非常糟糕:进行此运行所需的开销会使性能下降100-1000倍!使用分数的另一种方法也有严重的缺点:你需要实现gcd和kgv,不幸的是,分子或分母中的每个素数都有很好的机会破坏你的数字,扼杀你的性能。

因此,根据我的经验,它们是性能方面最差的选择。

我建议使用MPFR库,它是C和C++中速度最快的AP包之一。

有理数不会给出任意精度,而是给出精确的答案。然而,就存储而言,它们更昂贵,使用它们的某些操作变得昂贵,并且一些操作根本不允许,例如取平方根,因为它们不一定会产生合理的答案。

就我个人而言,我认为在你的情况下,AP浮动会更合适。