所以我在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
如果有人能给我一些关于这种类型的东西很有见解,那我只是总是使用小数数学,但似乎有问题。
答案 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,这大约是结果中的误差。