将“无穷大”浮点数转换为整数

2 投票
4 回答
1818 浏览
提问于 2025-04-18 00:27

我正在尝试检查一个数字是否是完全平方数。不过,我遇到的问题是,我要处理的数字非常非常大,以至于Python认为它是无穷大。我的代码在处理到大约1.1乘以10的154次方时就返回了“Inf”(无穷大)。有没有办法解决这个问题呢?下面是我的代码,lst变量里面存放了一堆非常非常非常大的数字。

import math
from decimal import Decimal
def main():
for i in lst:
    root = math.sqrt(Decimal(i))
    print(root)
    if int(root + 0.5) ** 2 == i:
       print(str(i) + " True")

4 个回答

1

math.sqrt() 函数会把你输入的数字转换成 Python 的浮点数,这种浮点数的最大值大约是 10 的 308 次方。

你可能应该考虑使用 gmpy2 这个库。gmpy2 提供了非常快速的多精度计算。

如果你想检查一个数字是否是某个数的幂,可以使用 gmpy2.is_power() 函数。如果这个数字是一个完全的幂,它会返回 True。这个数字可能是立方或者五次方,所以你需要检查你感兴趣的那个幂。

>>> gmpy2.is_power(456789**372)
True

你可以使用 gmpy2.isqrt_rem() 来检查一个数字是否是完全平方数。

>>> gmpy2.isqrt_rem(9)
(mpz(3), mpz(0))
>>> gmpy2.isqrt_rem(10)
(mpz(3), mpz(1))

你可以使用 gmpy2.iroot_rem() 来检查任意的幂。

>>> gmpy2.iroot_rem(13**7 + 1, 7)
(mpz(13), mpz(1))
1

@casevh 的回答是对的——使用一个可以处理非常大整数的库。因为你在找平方数,所以你大概是在处理整数,使用浮点数类型(包括 decimal.Decimal)在某种意义上可以说是不太优雅的。

你绝对 不应该 使用 Python 的 float 类型;它的精度有限(大约 16 位小数)。如果你使用 decimal.Decimal,要小心指定精度(这取决于你的数字有多大)。

由于 Python 有一个大整数类型,你可以写一个相对简单的算法来检查一个数是否是平方数;下面是我实现的一个这样的算法,以及浮点数的问题示例,还有如何使用 decimal.Decimal 的方法。

import math
import decimal

def makendigit(n):
    """Return an arbitraryish n-digit number"""
    return sum((j%9+1)*10**i for i,j in enumerate(range(n)))  
x=makendigit(30)

# it looks like float will work...
print 'math.sqrt(x*x) - x: %.17g' % (math.sqrt(x*x) - x)
# ...but actually they won't
print 'math.sqrt(x*x+1) - x: %.17g' % (math.sqrt(x*x+1) - x)

# by default Decimal won't be sufficient...
print 'decimal.Decimal(x*x).sqrt() - x:',decimal.Decimal(x*x).sqrt() - x
# ...you need to specify the precision
print 'decimal.Decimal(x*x).sqrt(decimal.Context(prec=30)) - x:',decimal.Decimal(x*x).sqrt(decimal.Context(prec=100)) - x

def issquare_decimal(y,prec=1000):
    x=decimal.Decimal(y).sqrt(decimal.Context(prec=prec))
    return x==x.to_integral_value()

print 'issquare_decimal(x*x):',issquare_decimal(x*x)
print 'issquare_decimal(x*x+1):',issquare_decimal(x*x+1)

# you can check for "squareness" without going to floating point.
# one option is a bisection search; this Newton's method approach
# should be faster.

# For "industrial use" you should use gmpy2 or some similar "big
# integer" library.
def isqrt(y):
    """Find largest integer <= sqrt(y)"""
    if not isinstance(y,(int,long)):
        raise ValueError('arg must be an integer')
    if y<0:
        raise ValueError('arg must be positive')
    if y in (0,1):
        return y
    x0=y//2
    while True:
        # newton's rule
        x1= (x0**2+y)//2//x0
        # we don't always get converge to x0=x1, e.g., for y=3
        if abs(x1-x0)<=1:
            # nearly converged; find biggest
            # integer satisfying our condition
            x=max(x0,x1)
            if x**2>y:
                while x**2>y:
                    x-=1
            else:
                while (x+1)**2<=y:
                    x+=1
            return x
        x0=x1

def issquare(y):
    """Return true if non-negative integer y is a perfect square"""
    return y==isqrt(y)**2

print 'isqrt(x*x)-x:',isqrt(x*x)-x

print 'issquare(x*x):',issquare(x*x)
print 'issquare(x*x+1):',issquare(x*x+1)
3

math.sqrt(Decimal(i)) 换成 Decimal(i).sqrt(),这样可以避免你的 Decimal 类型变成 float 类型。

2

我觉得你应该看看这个叫做 BigFloat 的模块,比如:

import bigfloat as bf
b = bf.BigFloat('1e1000', bf.precision(21))
print bf.sqrt(b)

它会打印出 BigFloat.exact('9.9999993810013282e+499', precision=53) 的结果。

撰写回答