哪个更准确,x **。5还是math.sqrt(x)?

时间:2009-05-08 23:33:01

标签: python floating-point

我最近发现x**.5math.sqrt(x)并不总是在Python中产生相同的结果:

Python 2.6.1 (r261:67517, Dec 4 2008, 16:51:00) [MSC v.1500 32 bit (Intel)]
on win32
>>> 8885558**.5 - math.sqrt(8885558)
-4.5474735088646412e-13

检查低于10 ** 7的所有整数,这两种方法对几乎正好0.1%的样本产生了不同的结果,对于较大的数字,误差的大小增加(缓慢)。

所以问题是,哪种方法更准确?

9 个答案:

答案 0 :(得分:25)

两者都不是更准确,它们都是等分的实际答案:

>>> (8885558**0.5)**2
8885557.9999999981
>>> sqrt(8885558)**2
8885558.0000000019

>>> 2**1023.99999999999
1.7976931348498497e+308

>>> (sqrt(2**1023.99999999999))**2
1.7976931348498495e+308
>>> ((2**1023.99999999999)**0.5)**2
1.7976931348498499e+308

>>> ((2**1023.99999999999)**0.5)**2 - 2**1023.99999999999
1.9958403095347198e+292
>>> (sqrt(2**1023.99999999999))**2 - 2**1023.99999999999
-1.9958403095347198e+292

http://mail.python.org/pipermail/python-list/2003-November/238546.html

  

数学模块包装平台C.   库的数学函数相同   名称;如果math.pow()最有用   你需要(或只是想)高   与C扩展的兼容性   调用C pow()

     

__builtin__.pow()是Python的中缀**的实现   运营商,并处理复杂的   数字,无界整数幂,和   模幂运算(C   pow()没有处理任何这些。)

**更完整。 math.sqrt可能只是sqrt的C实现,可能与pow有关。

答案 1 :(得分:11)

pow函数和math.sqrt()函数都可以计算比默认浮点类型可以存储的结果更准确的结果。我认为您看到的错误是浮点数学的局限性而不是函数的不准确性的结果。另外,因为当取7位数的平方根时,差异是~10 ^( - 13)?即使是最准确的物理计算也很少需要许多有效数字...

使用math.sqrt()的另一个原因是它更容易阅读和理解,这通常是以某种方式做事的一个很好的理由。

答案 2 :(得分:7)

使用decimal查找更精确的平方根:

>>> import decimal
>>> decimal.getcontext().prec = 60
>>> decimal.Decimal(8885558).sqrt()
Decimal("2980.86531061032678789963529280900544861029083861907705317042")

答案 3 :(得分:4)

每当你在一个语言中构建的两个函数之间做出选择时,更具体的函数几乎总是等于或优于通用函数(因为如果情况更糟,编码器就会实现它在通用功能方面)。 Sqrt比通用取幂更具体,因此您可以期待它是更好的选择。它至少就速度而言。在准确性方面,你没有足够的精确度来处理你的数字。

注意:为了澄清,在Python 3.0中sqrt更快。在旧版本的Python中速度较慢。见Which is faster in Python: x**.5 or math.sqrt(x)?的J.F. Sebastians测量结果。

答案 4 :(得分:3)

这必须是某种特定于平台的东西,因为我得到了不同的结果:

Python 2.5.1 (r251:54863, Jan 13 2009, 10:26:13) 
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
>>> 8885558**.5 - math.sqrt(8885558)
0.0

您使用的是哪个版本的python以及什么操作系统?

我的猜测是它与推广和演员有关。换句话说,因为你正在做8885558 **。5,所以必须将8885558提升为浮点数。根据操作系统,处理器和Python的版本,所有这些都有不同的处理方式。欢迎来到浮点运算的精彩世界。 : - )

答案 5 :(得分:3)

我在Win XP Python 2.5.1上遇到了同样的问题,而我没有使用32位Gentoo Python 2.5.4。这是C库实现的问题。

现在,在Win上,math.sqrt(8885558)**2提供了8885558.0000000019,而(8885558**.5)**2提供了8885557.9999999981,这似乎与相同的epsilon相同。

我说人们不能说哪一个是“更好”的选择。

答案 6 :(得分:1)

我没有得到同样的行为。也许错误是平台特定的? 在amd64上我得到了这个:

Python 2.5.2 (r252:60911, Mar 10 2008, 15:14:55) 
[GCC 3.3.5 (propolice)] on openbsd4
Type "help", "copyright", "credits" or "license" for more information.
>>> import math
>>> math.sqrt(8885558) - (8885558**.5)
0.0
>>> (8885558**.5) - math.sqrt(8885558)
0.0

答案 7 :(得分:1)

理论上,math.sqrt应该比math.pow具有更高的精度。参见牛顿计算平方根[0]的方法。但是,python float(或C double)的十进制数字的限制可能会掩盖差异。

[0] http://en.wikipedia.org/wiki/Integer_square_root

答案 8 :(得分:0)

我执行了相同的测试并得到了相同的结果,10000个中有10103个差异。这是在Windows中使用Python 2.7。

不同之处在于四舍五入。我相信当两个结果不同时,它只有一个ULP,这是浮点数的最小可能差异。真正的答案在于两者之间,但是float无法准确表示它并且必须是四舍五入的。

如另一个答案中所述,decimal module可用于获得比float更高的准确度。我利用它来更好地了解真正的错误,并且在所有情况下sqrt都比**0.5更接近。虽然不是很多!

>>> s1 = sqrt(8885558)
>>> s2 = 8885558**0.5
>>> s3 = decimal.Decimal(8885558).sqrt()
>>> s1, s2, s3
(2980.865310610327, 2980.8653106103266, Decimal('2980.865310610326787899635293'))
>>> s3 - decimal.Decimal(s1)
Decimal('-2.268290468226740188598632812E-13')
>>> s3 - decimal.Decimal(s2)
Decimal('2.2791830406379010009765625E-13')