使用 Haskell 中的系列的自然对数给出不精确的结果

Natural Logarithm using series in Haskell Gives Imprecise Results

所以我写了两个函数来计算变量 x 的自然对数,在将增量和的上限增加到 33000 之后,函数仍然 return 在 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]]

测试结果:

log 3
1.0986122886681098
lnOfx 3
1.0986125
lnOfx2 3
1.0986122

log 2
0.6931471805599453
lnOfx 2
0.6931472
lnOfx2 2
0.6931473

那么为什么结果会不同,正确的方法是什么(就像 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]]

在我的测试中,这足以让 print (lnOfx 3.0)print (log 3.0) 显示所有相同的数字。

但总的来说,我会推荐阅读数值分析书籍以了解更多有关此类问题的信息。