如何避免浮点错误?

问题描述 投票:22回答:2

我正在尝试编写一个函数来近似平方根(我知道有数学模块...我想自己做),并且我被浮点运算搞砸了。如何避免这种情况?

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中正确处理浮点计算。

python python-3.x floating-point floating-point-precision
2个回答
31
投票

这确实与Python无关-使用硬件的二进制浮点算法,您会在任何语言中看到相同的行为。首先read the docs

阅读之后,您会更好地理解,您正在not在代码中添加百分之一。这正是您要添加的内容:

>>> 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')

并不太准确,但是在简单的示例中可能并不奇怪,因为现在它添加了[[精确十分之一。

一种替代方法是坚持使用浮点数并添加

is

可以精确表示为二进制浮点数的值:I/2**J形式的值。例如,代替添加0.01,而添加0.125(1/8)或0.0625(1/16)。然后查找“牛顿法”以计算平方根;-)

0
投票
我的意思是,存在诸如decimalfractions之类的模块。但是

我做了一个针对此类问题的课程。此类仅解决加,减,乘,底除法,除法和模数。但是它是很容易可扩展。它基本上将浮点数转换为一个列表([浮点数,十的幂乘以浮点数得到一个整数])并从那里进行算术运算。整数比python中的浮点数更准确。 这就是该类所利用的。因此,事不宜迟,这里是代码:

class decimal(): # TODO: # OPTIMISE: code to maximize performance """ Class decimal, a more reliable alternative to float. | v0.1 ============================================================ Python's floats (and in many other languages as well) are pretty inaccurate. While on the outside it may look like this: .1 + .1 + .1 But on the inside, it gets converted to base 2. It tells the computer, "2 to the power of what is 0.1?". The computer says, "Oh, I don't know; would an approximation be sufficient?" Python be like, "Oh, sure, why not? It's not like we need to give it that much accuracy." And so that happens. But what they ARE good at is everything else, including multiplying a float and a 10 together. So I abused that and made this: the decimal class. Us humans knows that 1 + 1 + 1 = 3. Well, most of us anyway but that's not important. The thing is, computers can too! This new replacement does the following: 1. Find how many 10 ^ n it takes to get the number inputted into a valid integer. 2. Make a list with the original float and n (multiplying the by 10^-n is inaccurate) And that's pretty much it, if you don't count the adding, subtracting, etc algorithm. This is more accurate than just ".1 + .1 + .1". But then, it's more simple than hand-typing (.1 * 100 + .01 * 100 + .1 * 100)/100 (which is basically the algorithm for this). But it does have it's costs. -------------------------------------------------------------------------- BAD #1: It's slightly slower then the conventional .1 + .1 + .1 but it DOES make up for accuracy BAD #2: It's useless, there are many libraries out there that solves the same problem as this. They may be more or less efficient than this method. Thus rendering this useless. -------------------------------------------------------------------------- And that's pretty much it! Thanks for stopping by to read this doc-string. -------------------------------------------------------------------------- Copyright © 2020 Bryan Hu Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sub-license, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ def __init__(self, number): super(decimal, self).__init__() if number is iter: processed = float(number[0]) else: processed = float(number) x = 10 while round(processed * x) != processed * x: x *= 10 self.number = [processed, x] def __add__(self, other): the_other_number, num = list(other), list(self.number) try: maximum = max( float(num[1]), float(the_other_number[1])) return decimal( (num[0] * maximum + the_other_number[0] * maximum) / maximum) except IndexError: raise "Entered {}, which has the type {},\ is not a valid type".format( other, type(other)) def __float__(self): return float(self.number[0]) def __bool__(self): return bool(self.number[0]) def __str__(self): return str(self.number) def __iter__(self): return (x for x in self.number) def __repr__(self): return str(self.number[0]) def __sub__(self, other): the_other_number, num = list(other), list(self.number) try: maximum = max( float(num[1]), float(the_other_number[1])) return decimal( (num[0] * maximum - the_other_number[0] * maximum) / maximum) except IndexError: raise "Entered {}, which has the type {},\ is not a valid type".format( other, type(other)) def __div__(self, other): the_other_number, num = list(other), list(self.number) try: maximum = max( float(num[1]), float(the_other_number[1])) return decimal( ((num[0] * maximum) / ( the_other_number[0] * maximum)) / maximum) except IndexError: raise "Entered {}, which has the type {},\ is not a valid type".format( other, type(other)) def __floordiv__(self, other): the_other_number, num = list(other), list(self.number) try: maximum = max( float(num[1]), float(the_other_number[1])) return decimal( ((num[0] * maximum) // ( the_other_number[0] * maximum)) / maximum) except IndexError: raise "Entered {}, which has the type {},\ is not a valid type".format( other, type(other)) def __mul__(self, other): the_other_number, num = list(other), list(self.number) try: maximum = max( float(num[1]), float(the_other_number[1])) return decimal( ((num[0] * maximum) * ( the_other_number[0] * maximum)) / maximum) except IndexError: raise "Entered {}, which has the type {},\ is not a valid type".format( other, type(other)) def __mod__(self, other): the_other_number, num = list(other), list(self.number) try: maximum = max( float(num[1]), float(the_other_number[1])) return decimal( ((num[0] * maximum) % ( the_other_number[0] * maximum)) / maximum) except IndexError: raise "Entered {}, which has the type {},\ is not a valid type".format( other, type(other)) # Pastebin: https://pastebin.com/MwzZ1W9e
© www.soinside.com 2019 - 2024. All rights reserved.