【问题标题】:Floats vs rationals in arbitrary precision fractional arithmetic (C/C++)任意精度小数运算 (C/C++) 中的浮点数与有理数
【发布时间】:2012-08-03 15:23:37
【问题描述】:

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

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

【问题讨论】:

  • 你真的需要一个浮点数吗?浮点通常是指提供非均匀精度的表示:在0 周围更加“集中”,随着我们远离0 而下降,并且在范围的远端出现灾难性的“稀疏”。你真的需要这个属性吗?还是您只是使用 floating-point 作为任何小数的通用术语?换句话说,为什么不考虑定点算术?
  • @Desmond Hume:正如我在回答中所说,没有定点库的原因是定点不需要专用的“定点”库。定点算术只是在所有数据都乘以某个常数因子(稍作调整)之后的整数算术。换句话说,任何大整数库(同样:关键字是 integer)同时用作固定宽度小数库。
  • @DesmondHume:定点只是分数/有理数的特例/优化。
  • @AndreyT:至少有一个固定点 wrapper 肯定是有好处的。定点的乘法/除法只是将它们相乘。 (还有 IO 和转化等)
  • 顺便说一句,浮点数也是有理数,分母被限制为 2 的幂,这使得归一化变得便宜(移位而不是除以 gcd)。

标签: c++ c performance floating-point arbitrary-precision


【解决方案1】:

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

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

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

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

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

那么,你为什么只考虑浮点和有理表示。有什么东西阻止你考虑定点表示吗?

【讨论】:

  • 我会注意到,当您处理任意精度的数字时,浮点数和定点数之间实际上没有(性能)差异。
【解决方案2】:

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

这是为了回应您对理论利弊的提示。

我知道您没有询问内存使用情况,但这里有一个理论上的比较,以防其他人感兴趣。如上所述,有理数专门研究可以简单地用小数表示法表示的数字,例如1/3492113/203233,而浮点数专门研究易于用2 次方的科学计数法表示的数字,例如5*2^4591537*2^203233。以各自的人类可读形式表示数字所需的 ascii 输入量与其内存使用量成正比。

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

【讨论】:

    【解决方案3】:

    无论哪种方式,您都需要任意大小的整数相乘。这将是您性能的主要因素,因为它的复杂性比O(n*log(n)) 差。诸如对齐操作数、加减大整数之类的事情是O(n),所以我们将忽略这些。

    对于简单的加法和减法,您不需要浮点数的乘法* 和有理数的 3 次乘法。花车赢了。

    对于乘法,浮点数需要 1 次乘法,有理数需要 2 次乘法。花车有优势。

    除法有点复杂,理性可能会在这里胜出,但这绝不是确定的。我会说这是平局。

    所以总的来说,恕我直言,加法至少是 O(n*log(n)) 用于有理数,O(n) 用于浮点数显然使浮点表示获胜。

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

    【讨论】:

    • 您不能声称浮点加法不需要乘法,因为实际上您可能必须在加法之前进行相对标准化。
    • 您确定使用 Karatsuba 算法有效实现的 2 次整数乘法会比 1 次“浮点”乘法慢吗?
    • @MarkB:如果你够聪明的话,这可以通过轮班来完成。 Desmond:我认为“浮点”乘法需要两个整数乘法。
    • @MooingDuck 浮点乘法可能比整数乘法稍微便宜一些。这是因为您通常不需要产品的下半部分 - 这为优化留出了空间。
    • @MarkB 正如 MooingDuck 所说:你可以通过移动来做到这一点。即使你不使用 2 的幂作为底数,你仍然可以通过一个数字乘以一个数字,这也是 O(n)。
    【解决方案4】:

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

    第一个提议“模拟双精度”听起来像是交错精度:使用一个双精度数组,其和是定义的数字。 Douglas M. Priest 有一篇论文“Algorithms for Arbitrary Precision Floating Point Arithmetic”描述了如何实现这种算法。我实现了这一点,但我的经验非常很糟糕:进行此运行所需的开销会使性能下降 100-1000 倍! 使用小数的另一种方法也有严重的缺点:您需要实现 gcd 和 kgv,不幸的是,分子或分母中的每个素数都有很好的机会破坏您的数字并破坏您的性能。

    因此,根据我的经验,它们是最糟糕的选择,可以为性能做出选择。

    我推荐使用 MPFR 库,它是 C 和 C++ 中最快的 AP 包之一。

    【讨论】:

      【解决方案5】:

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

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

      【讨论】:

      • 虽然,如果你有一个好的理性库,你可以用它来处理continued fractions
      • @DesmondHume 不可能将sqrt(2) 或 Pi 表示为比率,因为它们是无理数。对于大多数多项式,只能将答案近似为有理数。你提到的那些算法是无限级数,需要无限多的步骤才能得到准确的值(因此不可能得到数值)。
      • @lvella 没有足够的内存足以完全精确地表示 Pi,无论是有理数还是“浮点数”,它们都是 both 的近似值。是什么真正阻止了设置 sqrt(2) 停止计算之后的最大存储字节数?字节数是任意的,这完全符合任意精度算术的思想。
      • @DesmondHume 当然,要完全精确地表示sqrt(361/9409) 需要多少内存?使用“浮点”表示将占用无限内存,但使用比率仅需要 2 个小整数。 @Qnan 应该说的是,除非您仍然想要确切的结果,否则不允许某些操作。之所以如此,是因为您可以用分数表示的数字范围大于您可以用“浮点数”表示的数字范围。通过使用有理数并避免需要近似的运算,您可以轻松保证不会发生错误并且您的结果始终是准确的。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-30
      • 1970-01-01
      • 2016-11-06
      • 1970-01-01
      • 1970-01-01
      • 2010-12-19
      相关资源
      最近更新 更多