为了一些大学作业,我需要用级数来近似一些数字 - 比如欧拉常数。因此,我需要加上非常小的数字,但是我在精度方面遇到了问题。如果这个数字非常小,它不会影响结果。
real s; //sum of all previous terms
ulong k; //factorial
s += 1.0/ k;
每一步之后,k都会变得更小,但是在第10轮之后,结果不再改变,停留在2.71828。
为了一些大学作业,我需要用级数来近似一些数字 - 比如欧拉常数。因此,我需要加上非常小的数字,但是我在精度方面遇到了问题。如果这个数字非常小,它不会影响结果。
real s; //sum of all previous terms
ulong k; //factorial
s += 1.0/ k;
每一步之后,k都会变得更小,但是在第10轮之后,结果不再改变,停留在2.71828。
固定精度浮点类型,即由CPU的浮点单元本地支持的类型(float
、double
、real
),对于需要多位精度的计算并不优秀,比如你所提供的例子。
问题在于这些浮点类型具有有限数量的精度数字(实际上是二进制数字),这限制了可以被此数据类型表示的数字长度。 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
,因为从a
和b
的加法中增加的精度位被截断了。
这里有一个解释这个概念的另一个问题,可能比我的解释更好。
这个问题的答案是任意精度算术。不幸的是,任意精度算术的支持并没有在CPU硬件中;因此,在你的编程语言中通常也没有支持。然而,有许多库支持任意精度浮点类型和你想要执行的数学运算。查看这个问题以获取一些建议。今天你可能找不到任何特定于D的库来实现这个目的,但有很多C库(如GMP、MPFR等)应该很容易单独使用,如果你可以找到其中一个的D绑定,那么使用它们会更加容易。
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];