您使用的迭代公式有点病态:随着迭代的进行,数量n变得比an大得多。所以在表达式中math.sqrt(n**2-4*an**2)-n

平方根的结果将接近n,因此外部减法是两个几乎相等的量的减法(在相对意义上)。现在,如果您使用常规的Python float进行计算,它有16位小数位的精度,那么随着迭代的进行,减法将给您一个只精确到少数数位的结果。有关这个问题的更一般性的讨论,请参见loss of significance上的Wikipedia页面。你知道吗

短篇故事:要使用最初编写的公式获得d位数的pi,您需要在中间计算中使用更多的d位数。通过一点工作,您可以表明您需要在内部使用略多于2d位的精度,以便获得d位精确的pi。即使这样,你也必须小心,只使用你需要的尽可能多的迭代:太多的迭代和精度将再次丢失,无论你使用多少中间精度。你知道吗

但是这里有一个更好的替代方法,比将中间精度加倍,那就是重写你的公式,这样就可以避免失去意义。如果将sqrt(n**2-4*an**2)-n乘以共轭表达式sqrt(n**2-4*an**2)+n,则得到-4*an**2。所以原始的差异可以重写为-4*an**2/(sqrt(n**2-4*an**2)+n)。将其插入原始公式并稍加简化,将导致一个如下所示的迭代步骤:def a2n(an, n):

return an*math.sqrt(2*n/(math.sqrt(n*n-4*an*an)+n))

从数学上讲,它的计算方法与a2n函数的计算方法完全相同,但从数值的角度来看,它的表现要好得多。你知道吗

如果您使用这个迭代步骤来代替原来的步骤,您将看到更小的舍入误差,并且您应该能够仅使用Python float就获得高达15位的精度。实际上,用这个新的迭代运行代码,我在30次迭代后得到一个3.1415926535897927的值,这仅仅是一个ulp(最后的单位)对pi的最佳双精度近似值。你知道吗

要获得更多的数字,我们需要使用^{}模块。下面是一个代码片段,基于您的代码,但是使用我建议的迭代修改计算,使用decimal模块获得精确到51位有效数字的pi值。它使用55位有效数字的内部精度,以允许舍入误差的累积。你知道吗

from decimal import getcontext
context = getcontext()
context.prec = 55 # Use 55 significant digits for all operations.
sqrt = context.sqrt # Will work for both ints and Decimal objects,
# returning a Decimal result.
def step(an, n):
return an*sqrt(2*n/(sqrt(n*n-4*an*an)+n)), n*2
def compute_pi(iterations):
value, round = 2, 4
for k in range(iterations):
value, round = step(value, round)
return value
pi_approx = compute_pi(100)
print("pi = {:.50f}".format(pi_approx))
结果如下:pi = 3.14159265358979323846264338327950288419716939937511

对于原始公式,中间计算的精度需要超过100。你知道吗