在函数类型定义中使用小数来进行数学计算 - 获取错误

时间:2014-09-08 16:38:22

标签: haskell numerical-methods

所以我在haskell中写了一个简单的程序来近似使用haskell的一个变量函数的二阶导数

centerDifferenceSecondDerivative :: (Fractional a)=>(a->a)->a->a->a
centerDifferenceSecondDerivative f a h = ((f (a+h)) -2*(f a) + (f (a-h)))/(h^2)

我很确定这个问题是一个类型问题,因为我得到了下面的奇怪结果。

*Main> centerDifference (\x->(x)) 1 0.000001
    0.9999999999732445
    *Main> centerDifference (\x->(0)) 1 0.000001
    0.0
    *Main> centerDifference (\x->(4*x^2)) 1 0.000001
    8.000000000008
    *Main> centerDifference (\x->(4*x^2)) 3 0.000001
    24.00000000335467
    *Main> centerDifference (\x->(cos x)) 0 0.000001
    0.0
    *Main> centerDifference (\x->(sin x)) 0 0.000001
    0.9999999999998334
    *Main> centerDifference (\x->(sin x)) 0 0.00000000001
    1.0
    *Main> centerDifference (\x->(x^3)) 0 0.00000000001
    9.999999999999999e-23
    *Main> centerDifference (\x->(x^3)) 1 0.00000000001
    3.000000248221113
    *Main> centerDifference (\x->(x^3)) 2 0.00000000001
    12.000000992884452
    *Main> centerDifference (\x->(x^3)) 3 0.00000000001
    26.999913416148047
    *Main> centerDifference (\x->(x^3)) 4 0.00000000001
    48.00000397153781
    *Main> centerDifference (\x->(x^3)) 5 0.00000000001
    74.99991738768585
    *Main> centerDifference (\x->(x)) 5 0.00000000001
    1.000000082740371
    *Main> centerDifference (\x->(x)) 20 0.00000000001
    1.000088900582341
    *Main> centerDifference (\x->(x)) 100 0.00000000001
    1.000444171950221
    *Main> centerDifference (\x->(x)) 100 0.1
    0.9999999999999432

如果有人能给我一些关于这种类型的东西很有见解,那我只是总是使用小数数学,但似乎有问题。

1 个答案:

答案 0 :(得分:4)

您正在看到浮点运算的截断错误 - 与类型无关。

我假设您将centerDifference定义为 centerDifference f a h =((f(a + h)) - (f(a-h)))/ 2h

使用有限差分公式精确计算导数实际上很棘手。极小的h值并不好,因为它们会引入较大的量化误差。 h的值太大,公式与限制值不够接近。你不能赢。通过h的最佳选择,结果通常精确到约2/3机器精度。

首先,你应该为h选择一个在其二进制浮点上有许多尾随0的值 - 尝试2 ^ -17。最佳值取决于a点处函数的行为。关于数值微分的一个很好的讨论可以在C第186页的数字食谱中找到:google it,你会发现这本书的pdf。它深入讨论了这一现象。

这里有一个解释为什么第一个结果中的错误比你预期的大(大约3 * 10 ^ -11,当你可能认为你会得到一个接近机器精度的误差~10 ^ -16 )。

双浮点数可以表示0.5≤x≤1之间的2 ^ 52个数中的任何一个。 1.0。由于间隔长度为1/2,因此这些数字相差2 ^ -53。在1.0< = x<之间存在2 ^ 52个浮点数。 2.0。由于该间隔长度为1,因此这些数字相差2 ^ 52。粗略地说,计算1-h的结果比计算1 + h的结果更准确。因此(计算结果)1-h和1 + h在1左右不对称。减去(1 + h) - (1-h)产生误差~2 ^ -53。然后你将它除以2 * 10 ^ -6,将误差扩展到约5 * 10 ^ -11,这大约是结果中的误差。