使用Haskell级数的自然对数给出了不精确的结果

使用Haskell级数的自然对数给出了不精确的结果,haskell,math,libm,Haskell,Math,Libm,因此,我编写了两个函数来计算变量x的自然对数,将增量和的上限增加到33000后,与Prelude中导入的默认日志函数相比,这些函数仍然返回在ghci中测试的不精确结果,以下是代码定义: lnOfx :: Float -> Float lnOfx x = netSum f 1 33000 where f i = 2*(1/oddTerm)*((x-1)/(x+1))**oddTerm where oddTerm = 2*i-1 netSum

因此,我编写了两个函数来计算变量x的自然对数,将增量和的上限增加到33000后,与Prelude中导入的默认日志函数相比,这些函数仍然返回在ghci中测试的不精确结果,以下是代码定义:

lnOfx :: Float -> Float
lnOfx x = netSum f 1 33000
    where f i = 2*(1/oddTerm)*((x-1)/(x+1))**oddTerm
            where oddTerm = 2*i-1
          netSum f minB maxB = sum [f i | i <- [minB .. maxB]]

lnOfx2 :: Float -> Float
lnOfx2 x = netSum f 1 33000
       where f i = (1/i)*((x-1)/x)**i
             netSum f minB maxB = sum [f i | i <- [minB .. maxB]]

那么,为什么结果会有所不同?像Prelude中的log函数那样,正确计算自然对数的正确方法是什么?

浮点数学很棘手。其中一个可能导致精度损失的因素是添加不同量级的数字。例如,在从i=25开始的算法中,求和中的项足够小,以至于它们不再产生差异:

-- 25t term:    
let f x i = let oddTerm = 2*i-1 in 2*(1/oddTerm)*((x-1)/(x+1))**oddTerm
let y = f 3.0 25

-- summation up to 24 item
let s = 1.098612288668109

-- this will return True, surprisingly!
s + y == s
你可以做一件事来缓解这个问题,那就是按相反的顺序添加数字,这样小的数字在添加到大的数字之前就被添加到一起了

lnOfx :: Float -> Float
lnOfx x = netSum f 1 33000
    where f i = 2*(1/oddTerm)*((x-1)/(x+1))**oddTerm
            where oddTerm = 2*i-1
          netSum f minB maxB = sum $ reverse [f i | i <- [minB .. maxB]]
在我的测试中,这已经足够了,所以打印lnOfx 3.0和打印日志3.0显示了所有相同的数字


但总的来说,我建议读一本数值分析书来了解更多关于这类问题的知识。

浮点数学是很棘手的。其中一个可能导致精度损失的因素是添加不同量级的数字。例如,在从i=25开始的算法中,求和中的项足够小,以至于它们不再产生差异:

-- 25t term:    
let f x i = let oddTerm = 2*i-1 in 2*(1/oddTerm)*((x-1)/(x+1))**oddTerm
let y = f 3.0 25

-- summation up to 24 item
let s = 1.098612288668109

-- this will return True, surprisingly!
s + y == s
你可以做一件事来缓解这个问题,那就是按相反的顺序添加数字,这样小的数字在添加到大的数字之前就被添加到一起了

lnOfx :: Float -> Float
lnOfx x = netSum f 1 33000
    where f i = 2*(1/oddTerm)*((x-1)/(x+1))**oddTerm
            where oddTerm = 2*i-1
          netSum f minB maxB = sum $ reverse [f i | i <- [minB .. maxB]]
在我的测试中,这已经足够了,所以打印lnOfx 3.0和打印日志3.0显示了所有相同的数字


但总的来说,我建议读一本数值分析书来了解这类问题。

1浮点运算本质上是不精确的。2函数中的不精确部分仅仅是因为您使用的是Float而不是Double。@duplode感谢您的回答。它确实更精确,但在我将函数类型签名从float改为Double,然后与Prelude log进行比较之后,最后几位数字仍然相差很大。1浮点算法本质上是不精确的。2函数中的不精确部分仅仅是因为您使用的是Float而不是Double。@duplode感谢您的回答。它确实更精确,但在我将函数类型签名从float改为Double,然后与Prelude log进行比较之后,最后几位数字仍然相差很大。