8

我试图写一个函数来近似平方根(我知道有数学模块...我想自己做),并且我被浮点算术搞砸了。你怎么能避免这种情况?如何避免浮点错误?

def sqrt(num): 
    root = 0.0 
    while root * root < num: 
     root += 0.01 
    return root 

使用这有以下结果:

>>> sqrt(4) 
2.0000000000000013 
>>> sqrt(9) 
3.00999999999998 

我知道我可以只使用round(),但我希望能够让这个真正准确。我希望能够计算出6位或7位数字。如果我四舍五入,这是不可能的。我想了解如何在Python中正确处理浮点计算。

+0

也许尝试[小数](http://docs.python.org/2/library/decimal.html)模块,这是精确设计? – Michael0x2a

回答

15

这实际上与Python没有任何关系 - 使用硬件的二进制浮点算法,您会在任何语言中看到相同的行为。第一个read the docs

读完之后,你会更好地理解你是而不是在你的代码中增加了百分之一。这是您要添加什么:

>>> from decimal import Decimal 
>>> Decimal(.01) 
Decimal('0.01000000000000000020816681711721685132943093776702880859375') 

该字符串表示的二进制浮点(在C“双精度”)的精确十进制值近似准确的十进制值0.01。你真的添加的东西比1/100大一点。

控制浮点数字错误是被称为“数值分析”的字段,是一个非常庞大而复杂的话题。只要您对浮点数只是十进制值的近似值而感到惊讶,请使用decimal模块。这会为你带来一个“浅”问题的世界。例如,给定这个小修改你的函数:

from decimal import Decimal as D 

def sqrt(num): 
    root = D(0) 
    while root * root < num: 
     root += D("0.01") 
    return root 

则:

>>> sqrt(4) 
Decimal('2.00') 
>>> sqrt(9) 
Decimal('3.00') 

这不是真的更准确,但可能是简单的例子那么令人惊讶,因为现在它的加入正好一一-hundredth。

另一种方法是坚持浮动并添加的东西可以完全表示为二进制浮点形式:值为I/2**J。例如,不要添加0.01,而要添加0.125(1/8)或0.0625(1/16)。

然后查找“牛顿法”,用于计算平方根;-)

+0

为了记录我读过文档,我确实已经知道存储二进制表示的整个浮点精度。我忘记了牛顿的方法。你在这里捡起我所有的问题!我发现你的幸运日。我不知道Decimal模块是如何工作的。除了阅读源代码之外,还有什么方法可以找到答案? – Aerovistae

+1

好吧,'decimal'最初是用Python编写的,它处理十进制数字(0,1,2,...,9)的列表。非常模仿我们如何在纸上做算术! “浮点”只需要向表示添加一个(十进制)指数,然后非常小心;-)当前的“十进制”模块用C编码,并且更加模糊:-( –

+0

如您所说,我尝试过使用十进制模块来解决'4 - 3.2'。 一个十进制=(4) B =十进制(3.2) 但 一个 - B结果为小数( '0.7999999999999998223643160600') – Srinesh