34

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

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

4

2 回答 2

44

这实际上与 Python 无关——使用硬件的二进制浮点算法,您会在任何语言中看到相同的行为。首先阅读文档

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

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

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

于 2013-10-20T04:18:20.383 回答
3

我的意思是,有诸如decimal和之类的模块fractions。但是我为这些问题开设了一个课程。此类仅解决加法、减法、乘法、地板除法、除法和模数。但它很容易扩展。它基本上将浮点数转换为一个列表([浮点数,十的幂乘以浮点数得到一个整数])并从那里进行算术运算。整数比 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
于 2020-06-13T18:23:08.523 回答