除以haskell中的小数量

除以haskell中的小数量,haskell,Haskell,我有一个关于Haskell中数值运算的问题 我有一个基本功能: derv::(Num a, Fractional a) => (a -> a) -> a -> a -> a derv f a deltax = ((f (a+deltax))-(f a))/deltax 当我测试它时,这是我得到的输出: *Main> derv (\x->x*x) 2 0.000000000000000001 0.0 *Main> derv

我有一个关于Haskell中数值运算的问题

我有一个基本功能:

derv::(Num a, Fractional a) => (a -> a) -> a -> a -> a
derv f a deltax = ((f (a+deltax))-(f a))/deltax
当我测试它时,这是我得到的输出:

    *Main> derv (\x->x*x) 2 0.000000000000000001
    0.0
    *Main> derv (\x->x*x) 2 0.00000000000001
    4.085620730620576
    *Main> derv (\x->x*x) 2 0.0000000001
    4.000000330961484
    *Main> derv (\x->x*x) 2 0.0001
    4.0001000000078335
    *Main> 

当除数变小时,它会使答案自动变为零,而不是更精确地收敛到4。我很好奇为什么会发生这种情况,特别是考虑到我的类型定义。

这可能是由于浮点舍入错误造成的。您可以使用固定点数,如图所示。

在您的代码中,
0.00000000000001
可能被
默认为
双精度
,导致 由于四舍五入,添加了
2
之后的精度损失

使用像
Rational
这样的精确表示不会出现相同的问题:

> import Data.Ratio
> derv (\x->x*x) 2 0.000000000000000001 :: Rational
4000000000000000001 % 1000000000000000000
> fromRational (derv (\x->x*x) 2 0.000000000000000001) :: Double
4.0

在最后一行中,计算增量比率后会出现精度损失,因此结果接近上面显示的精确分数。

这与haskell无关。阅读(请注意,如果您添加
::Rational
,则在第一次测试中不会得到0,因为ghci使用精确的有理数而不是浮点进行计算。)-顺便说一句,使用固定的
δx
是一种非常可怕的区分方式——通常,是一个更好的选择。
2+epsilon==2
在浮点运算中,对于足够小的
epsilon
。四舍五入确实很重要。旁注:中心差商
(f(a+deltax)-f(a-deltax))/(2*deltax)
收敛得更快。