所以我编写了两个函数来计算变量x的自然对数,在将增量和的上限增加到33000之后,函数仍然返回在ghci中测试的不精确结果,与从Prelude导入的默认日志函数相比,这里是代码定义:
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的日志函数)正确计算自然对数?
答案 0 :(得分:6)
浮点数学很棘手。导致精度损失的一个原因是增加了数量级别不同的数字。例如,在以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)
显示所有相同的数字。
但总的来说,我建议您阅读一本数值分析书,以了解有关此类问题的更多信息。