在使用Viete'计算Pi近似值时,卡在ocaml的无限循环中;s公式

在使用Viete'计算Pi近似值时,卡在ocaml的无限循环中;s公式,ocaml,pi,approximation,Ocaml,Pi,Approximation,我在计算给定精度的Pi近似值时遇到了问题。我已经得出结论,无限循环问题是由我的循环退出条件引起的,但是我不知道确切的问题是什么。在我看来,退出条件应该是 abs(current_aproximation - previous_approximation) < precision abs(当前近似值-先前近似值)

我在计算给定精度的Pi近似值时遇到了问题。我已经得出结论,无限循环问题是由我的循环退出条件引起的,但是我不知道确切的问题是什么。在我看来,退出条件应该是

abs(current_aproximation - previous_approximation) < precision
abs(当前近似值-先前近似值)
代码如下:

let pi(prec) = 
let rec loop(curr, prev) = 
    if(abs_float( (2. /. curr) -. (2. /. prev) ) < prec) then  // problematic line
        (2. /. curr)
    else
        loop(curr *. sqrt(2. +. curr) /. 2., curr)
    in loop(sqrt(0.5), 1.);;
让pi(prec)=
让rec循环(当前,上一个)=
如果(abs_float((2./.curr)-(2./.prev))

感谢您提供有关解决此问题的任何提示。

如果您修改代码以打印出
curr
的值,您会发现您很快就到达了一个固定点:

9.88131291682e-324
9.88131291682e-324
9.88131291682e-324
9.88131291682e-324
9.88131291682e-324
9.88131291682e-324
9.88131291682e-324
9.88131291682e-324
9.88131291682e-324
(...)
根据你的初始值应该是
(sqrt 2.)/。2.
而不是
sqrt(0.5)
。但即使进行了这种修改,也不可能要求精度小于
0.61
,而不达到前面描述的固定点


我的猜测是,
float
s不够精确,无法以这种方式表示此算法。

您正在计算一个不同的乘积:在一次迭代之后,您应该计算sqrt(2)/2*sqrt(2+sqrt(2))/2,但您计算sqrt(2)/2*sqrt(2+sqrt(2)/2)/2

这个算法怎么样

let pi prec =
   let rec p2 xn root =
     let nroot = sqrt(2. +. root) in
     let xm = xn *. (nroot /. 2.) in
     if (abs_float (( 2. /. xm ) -. (2. /. xn))) < prec
     then xm
     else p2 xm nroot
   in 2. /. (p2 1.0 0.0)

sqrt(2)/2
sqrt(0.5)
是相等的,浮动精度不会导致此类错误。算法实现错误。非常感谢。我一定错过了计算的那一部分。我还有一个附带的问题-当传入递归函数的参数较少时,代码的性能是否会提高?还是这只是个人偏好?您的意思是p2作为闭包绑定prec与将prec作为附加参数添加到内部函数之间的区别?我想这取决于编译器,但我可以想象闭包会被翻译成第二个版本。在这种特殊情况下,p2无论如何只创建一次。
# pi 0.1 ;;
- : float = 3.12144515225805197
# pi 0.01 ;;
- : float = 3.14033115695475251
# pi 0.001 ;;
- : float = 3.14127725093277288
# pi 0.0001 ;;
- : float = 3.14157294036709134