如何在D中执行高精度计算?

时间:2011-02-09 12:15:03

标签: floating-point d arbitrary-precision

对于一些普遍的工作,我必须近似一些数字 - 比如欧拉系列。因此我必须添加非常小的数字,但我的精度有问题。如果数字非常小,则不会影响结果。

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

s += 1.0/ k;

每一步之后,k变得更小,但在第10轮之后结果不再变化并且停留在2.71828

3 个答案:

答案 0 :(得分: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);

ab都是有效的浮点值,并且单独保留大约7位精度。但是当添加时,只保留最前面的7位数字,因为它被推回到浮动中:

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

所以c最终等于a,因为添加ab的精确数字会被打破。

Here's another explanation of the concept,可能比我的要好得多。


这个问题的答案是任意精度算术。不幸的是,对任意精度算术的支持不在CPU硬件中;因此,它(通常)不是您的编程语言。但是,有许多库支持任意精度浮点类型以及要对它们执行的数学运算。有关建议,请参阅this question。你今天可能找不到任何特定于D的库,但是有很多C库(GMP,MPFR等)应该很容易单独使用,如果你能找到它们就更是如此其中一个的D绑定。

答案 1 :(得分: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];

(最小堆会使这个速度加快。)

答案 2 :(得分:2)

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

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