Python 如何避免浮点错误?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19473770/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 13:50:29  来源:igfitidea点击:

How to avoid floating point errors?

pythonpython-3.xfloating-pointfloating-point-precision

提问by temporary_user_name

I was trying to write a function to approximate square roots (I know there's the math module...I want to do it myself), and I was getting screwed over by the floating point arithmetic. How can you avoid that?

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

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

Using this has these results:

使用它有以下结果:

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

I realize I could just use round(), but I want to be able to make this really accurate. I want to be able to calculate out to 6 or 7 digits. That wouldn't be possible if I'm rounding. I want to understand how to properly handle floating point calculations in Python.

我意识到我可以只使用round(),但我希望能够使其非常准确。我希望能够计算出 6 或 7 位数字。如果我四舍五入,那是不可能的。我想了解如何在 Python 中正确处理浮点计算。

采纳答案by Tim Peters

This really has nothing to do with Python - you'd see the same behavior in any language using your hardware's binary floating-point arithmetic. First read the docs.

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

After you read that, you'll better understand that you're notadding one one-hundredth in your code. This is exactly what you're adding:

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

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

That string shows the exact decimal value of the binary floating ("double precision" in C) approximation to the exact decimal value 0.01. The thing you're really adding is a little bigger than 1/100.

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

Controlling floating-point numeric errors is the field called "numerical analysis", and is a very large and complex topic. So long as you're startled by the fact that floats are just approximations to decimal values, use the decimalmodule. That will take away a world of "shallow" problems for you. For example, given this small modification to your function:

控制浮点数值错误是一个叫做“数值分析”的领域,是一个非常庞大而复杂的话题。只要您对浮点数只是十进制值的近似值这一事实感到震惊,请使用该decimal模块。这将为您带走一个“浅”问题的世界。例如,对您的函数进行这个小修改:

from decimal import Decimal as D

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

then:

然后:

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

It's not really more accurate, but may be less surprising in simple examples because now it's adding exactlyone one-hundredth.

它并不是更准确,但在简单的例子中可能不那么令人惊讶,因为现在它正好增加百分之一。

An alternative is to stick to floats and add something that isexactly representable as a binary float: values of the form I/2**J. For example, instead of adding 0.01, add 0.125 (1/8) or 0.0625 (1/16).

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

Then look up "Newton's method" for computing square roots ;-)

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