如何避免浮点错误?

时间:2013-10-20 03:56:11

标签: python python-3.x floating-point floating-point-precision

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

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

2 个答案:

答案 0 :(得分:20)

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

这不是更准确,但在简单的例子中可能不那么令人惊讶,因为现在它正在将完全添加到百分之一。

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

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

答案 1 :(得分:1)

我的意思是,有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