如何在D语言中进行高精度计算?

4

为了一些大学作业,我需要用级数来近似一些数字 - 比如欧拉常数。因此,我需要加上非常小的数字,但是我在精度方面遇到了问题。如果这个数字非常小,它不会影响结果。

real s;  //sum of all previous terms
ulong k; //factorial

s += 1.0/ k;

每一步之后,k都会变得更小,但是在第10轮之后,结果不再改变,停留在2.71828。

3个回答

9

固定精度浮点类型,即由CPU的浮点单元本地支持的类型(floatdoublereal),对于需要多位精度的计算并不优秀,比如你所提供的例子。

问题在于这些浮点类型具有有限数量的精度数字(实际上是二进制数字),这限制了可以被此数据类型表示的数字长度。 float 类型的极限约为7个十进制数字(例如3.141593);double 类型限制为14个数字(例如3.1415926535898);而 real 类型则有类似的限制(略大于 double)。

因此,向浮点值添加非常小的数字将导致这些数字丢失。看看当我们将以下两个浮点值相加时会发生什么:

float a = 1.234567f, b = 0.0000000001234567
float c = a + b;

writefln("a = %f b = %f c = %f", a, b, c);

无论是a还是b都是有效的浮点数,在独立的情况下,保留了大约7位有效数字。但是当它们相加时,只有前7位有效数字被保留,因为它们被转换回一个浮点数:

1.2345670001234567 => 1.234567|0001234567 => 1.234567
                              ^^^^^^^^^^^
                         sent to the bit bucket

所以,c最终等于a,因为从ab的加法中增加的精度位被截断了。

这里有一个解释这个概念的另一个问题,可能比我的解释更好。


这个问题的答案是任意精度算术。不幸的是,任意精度算术的支持并没有在CPU硬件中;因此,在你的编程语言中通常也没有支持。然而,有许多库支持任意精度浮点类型和你想要执行的数学运算。查看这个问题以获取一些建议。今天你可能找不到任何特定于D的库来实现这个目的,但有很多C库(如GMP、MPFR等)应该很容易单独使用,如果你可以找到其中一个的D绑定,那么使用它们会更加容易。


3
如果您需要一个能够使用本机类型运行的解决方案,您可以尝试始终添加大小相似的数字来获得合理的结果。一种方法是计算前X个系列项,然后反复用它们的和替换最小的两个数字:
auto data = real[N];
foreach(i, ref v; data) {
  v = Fn(i);
}

while(data.length > 1) {
  data.sort(); // IIRC .sort is deprecated but I forget what replaced it.
  data[1] += data[0];
  data = data[1..$];
}

return data[0];

(使用最小堆可以使其更快。)

太好了,我会尝试这种方法! - NaN

2

如前所述,您需要使用一些第三方多精度浮点算术库(我认为Tango或Phobos仅具有任意长度整数算术模块)。

dil是一个使用MPFR的D语言项目。您应该在那里找到绑定。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接