为什么我在浮点数指数运算中会收到“OverflowError: (34, 'Result too large')”或“OverflowError: (34, 'Numerical result out of range')”错误?

35

我尝试使用这段代码计算圆周率到多位小数:

def pi(): 
    pi = 0 
    for k in range(350): 
        pi += (4./(8.*k+1.) - 2./(8.*k+4.) - 1./(8.*k+5.) - 1./(8.*k+6.)) / 16.**k 
    return pi 
print(pi())

我为什么会收到一个错误,上面写着OverflowError: (34, 'Result too large')


for k in range(350): 太大了,导致出现了这个问题。 - karthikr
6个回答

43

Python的浮点数既不是任意精度也不是无限大小。当k = 349时,16.**k太大了 - 几乎是2^1400。幸运的是,decimal库允许任意精度并可以处理这样的大小:

import decimal
decimal.getcontext().prec = 100
def pi():
    pi = decimal.Decimal(0)
    for k in range(350):
        pi += (decimal.Decimal(4)/(decimal.Decimal(8)*decimal.Decimal(k+1))...)

21

您已经达到了平台对float类型的支持上限,可能是在 k = 256 之后:

>>> k = 256
>>> (4./(8.*k+1.) - 2./(8.*k+4.) - 1./(8.*k+5.) - 1./(8.*k+6.)) / 16.**k
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OverflowError: (34, 'Result too large')
>>> k = 255
>>> (4./(8.*k+1.) - 2./(8.*k+4.) - 1./(8.*k+5.) - 1./(8.*k+6.)) / 16.**k
3.19870064997e-313

参见 sys.float_info 以查看精度限制,但是无论如何,你不太可能在当前的CPU和操作系统组合中达到100个有效数字; 我的64位OS X的MacBook Pro仅支持15个有效数字。

使用decimal模块可以超越硬件限制。

from decimal import Decimal, localcontext

def pi(): 
    with localcontext() as ctx:
        ctx.prec = 100  # 100 digits precision
        pi = Decimal(0) 
        for k in range(350): 
            pi += (Decimal(4)/(Decimal(8)*k+1) - Decimal(2)/(Decimal(8)*k+4) - Decimal(1)/(Decimal(8)*k+5) - Decimal(1)/(Decimal(8)*k+6)) / Decimal(16)**k 
    return pi 

3

16.**256太大了,无法存储在双精度浮点数中。建议您将循环运行更少的次数,比如range(250),因为更大的k值对前100位数字没有贡献。

您可以尝试使用乘以16.*(-k)而不是除以16.*k的方法。对于大的k值,这个数字将被舍入为零,因此不会导致运行时错误。

我建议您使用numpy.power而不是**,它可以更好地处理溢出。例如,在您的代码中,numpy.power(16.,256)将评估为inf,将有限数除以inf得到零,就像上一段中建议的方法一样,避免了运行时错误。


2

我使用的是Python3.6 AMD64版本,我也遇到了这个问题。原因在于Python内置的float类型是双精度浮点数,占用64位。在大多数编程任务中,64位足够了,但在一些特殊任务中,如科学计算和大数据计算,64位不够用。


2

这是使用decimal库解决此问题的Python方案。该代码计算圆周率的一千位数字。

import decimal
def pi( prec = 10 ** 3 ):
    decimal.getcontext().prec = prec
    b =  decimal.Decimal(1)
    pi = 0
    for k in range(prec):
        pi += ( b*4/(8*k+1) - b*2/(8*k+4) - b*1/(8*k+5) - b*1/(8*k+6)) / 16**k
    return pi
print(pi())

这是一种仅使用内置任意大小整数的解决方案。它可以更高效地工作,并允许您计算出十万位数的圆周率。
def pi( prec = 10 ** 4 ):
    b = 10 ** prec
    pi = 0
    for k in range(prec):
        pi += ( b*4//(8*k+1) - b*2//(8*k+4) - b*1//(8*k+5) - b*1//(8*k+6)) // 16**k
    return pi
print(pi())

通过启动此代码,您可以向朋友炫耀自己已将一万个数字计算为圆周率 :)。

-1

如果需要近乎无限的精度,请使用十进制。

在某些罕见情况下,如果您正在执行n ** 2或类似操作。您可以通过将其转换为n * n来避免错误,因此根据您解决此问题的方式,这可能是一个可靠的解决方法。您的数字将被称为inf而不是抛出错误,**执行幂函数并且它是引发错误的函数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接