python 浮点数和小数点的小数位问题.Decimal

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/286061/
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-11-03 19:49:28  来源:igfitidea点击:

Decimal place issues with floats and decimal.Decimal

pythonfloating-pointdecimalfloating-accuracy

提问by darudude

I seem to be losing a lot of precision with floats.

我似乎失去了很多浮动的精度。

For example I need to solve a matrix:

例如我需要解决一个矩阵:

4.0x -2.0y 1.0z =11.0
1.0x +5.0y -3.0z =-6.0
2.0x +2.0y +5.0z =7.0

This is the code I use to import the matrix from a text file:

这是我用来从文本文件导入矩阵的代码:

f = open('gauss.dat')
lines =  f.readlines()
f.close()

j=0
for line in lines:
    bits = string.split(line, ',')
    s=[]
    for i in range(len(bits)):
        if (i!= len(bits)-1):
            s.append(float(bits[i]))
            #print s[i]
    b.append(s)
    y.append(float(bits[len(bits)-1]))

I need to solve using gauss-seidel so I need to rearrange the equations for x, y, and z:

我需要使用 gauss-seidel 求解,所以我需要重新排列 x、y 和 z 的方程:

x=(11+2y-1z)/4
y=(-6-x+3z)/5
z=(7-2x-2y)/7

Here is the code I use to rearrange the equations. bis a matrix of coefficients and yis the answer vector:

这是我用来重新排列方程的代码。b是系数矩阵,y是答案向量:

def equations(b,y):
    i=0
    eqn=[]
    row=[]
    while(i<len(b)):
        j=0
        row=[]
        while(j<len(b)):
            if(i==j):
                row.append(y[i]/b[i][i])
            else:
                row.append(-b[i][j]/b[i][i])
            j=j+1
        eqn.append(row)
        i=i+1
    return eqn

However the answers I get back aren't precise to the decimal place.

然而,我得到的答案并不精确到小数点。

For example, upon rearranging the second equation from above, I should get:

例如,重新排列上面的第二个等式后,我应该得到:

y=-1.2-.2x+.6z

What I get is:

我得到的是:

y=-1.2-0.20000000000000001x+0.59999999999999998z

This might not seem like a big issue but when you raise the number to a very high power the error is quite large. Is there a way around this? I tried the Decimalclass but it does not work well with powers (i.e, Decimal(x)**2).

这可能看起来不是什么大问题,但是当您将数字提高到非常高的幂时,误差会非常大。有没有解决的办法?我试过这Decimal门课,但它不能很好地与权力(即,Decimal(x)**2)一起工作。

Any ideas?

有任何想法吗?

回答by Doug Currie

IEEE floating point is binary, not decimal. There is no fixed length binary fraction that is exactly 0.1, or any multiple thereof. It is a repeating fraction, like 1/3 in decimal.

IEEE 浮点数是二进制的,而不是十进制的。不存在精确为 0.1 或其任何倍数的固定长度二进制小数。它是一个重复的分数,就像十进制的 1/3。

Please read What Every Computer Scientist Should Know About Floating-Point Arithmetic

请阅读每个计算机科学家应该了解的关于浮点运算的知识

Other options besides a Decimal class are

除了 Decimal 类之外的其他选项是

  • using Common Lisp or Python 2.6or another language with exact rationals

  • converting the doubles to close rationals using, e.g., frap

  • 使用 Common Lisp 或Python 2.6或其他具有精确有理数的语言

  • 使用例如frap将双打转换为封闭有理数

回答by Facebook Staff are Complicit

I'm not familiar enough with the Decimal class to help you out, but your problem is due to the fact that decimal fractions can often not be accurate represented in binary, so what you're seeing is the closest possible approximation; there's no way to avoid this problem without using a special class (like Decimal, probably).

我对 Decimal 类不够熟悉,无法为您提供帮助,但您的问题是由于十进制分数通常无法准确表示为二进制,所以您看到的是最接近的近似值;如果不使用特殊类(可能像 Decimal),就无法避免这个问题。

EDIT:What about the decimal class isn't working properly for you? As long as I start with a string, rather than a float, powers seem to work fine.

EDIT:十进制类不适合你怎么办?只要我从一个字符串而不是一个浮点数开始,power 似乎就可以正常工作。

>>> import decimal
>>> print(decimal.Decimal("1.2") ** 2)
1.44

The module documentationexplains the need for and usage of decimal.Decimalpretty clearly, you should check it out if you haven't yet.

模块文档解释了需要和使用decimal.Decimal非常清楚,你应该检查出来,如果你还没有。

回答by S.Lott

First, your input can be simplified a lot. You don't need to read and parse a file. You can just declare your objects in Python notation. Eval the file.

首先,您的输入可以简化很多。您不需要读取和解析文件。你可以只用 Python 符号声明你的对象。评估文件。

b = [
    [4.0, -2.0,  1.0],
    [1.0, +5.0, -3.0],
    [2.0, +2.0, +5.0],
]
y = [ 11.0, -6.0, 7.0 ]

Second, y=-1.2-0.20000000000000001x+0.59999999999999998z isn't unusual. There's no exact representation in binary notation for 0.2 or 0.6. Consequently, the values displayed are the decimal approximations of the original not exact representations. Those are true for just about every kind of floating-point processor there is.

其次,y=-1.2-0.20000000000000001x+0.5999999999999998z 并不罕见。0.2 或 0.6 的二进制表示法没有确切的表示。因此,显示的值是原始不精确表示的十进制近似值。对于几乎所有类型的浮点处理器来说都是如此。

You can try the Python 2.6 fractionsmodule. There's an older rationalpackage that might help.

您可以尝试 Python 2.6分数模块。有一个较旧的理性包可能会有所帮助。

Yes, raising floating-point numbers to powers increases the errors. Consequently, you have to be sure to avoid using the right-most positions of the floating-point number, since those bits are mostly noise.

是的,将浮点数提高到幂会增加错误。因此,您必须确保避免使用浮点数的最右边位置,因为这些位主要是噪声。

When displaying floating-point numbers, you have to appropriately round them to avoid seeing the noise bits.

显示浮点数时,您必须对它们进行适当的舍入以避免看到噪声位。

>>> a
0.20000000000000001
>>> "%.4f" % (a,)
'0.2000'

回答by Brian

I'd caution against the decimal module for tasks like this. Its purpose is really more dealing with real-world decimal numbers (eg. matching human bookkeeping practices), with finite precision, not performing exact precision math. There are numbers not exactly representable in decimal just as there are in binary, and performing arithmetic in decimal is also much slower than alternatives.

对于这样的任务,我会警告不要使用小数模块。它的目的实际上更多的是处理真实世界的十进制数(例如,匹配人类簿记实践),具有有限的精度,而不是执行精确的精度数学。与二进制一样,有些数字不能完全用十进制表示,而且用十进制执行算术也比替代方法慢得多。

Instead, if you want exact results you should use rational arithmetic. These will represent numbers as a numerator/denomentator pair, so can exactly represent all rational numbers. If you're only using multiplication and division (rather than operations like square roots that can result in irrational numbers), you will never lose precision.

相反,如果您想要精确的结果,您应该使用有理算术。这些将数字表示为分子/分母对,因此可以准确地表示所有有理数。如果您只使用乘法和除法(而不是像平方根这样会导致无理数的运算),那么您将永远不会失去精度。

As others have mentioned, python 2.6 will have a built-in rational type, though note that this isn't really a high-performing implementation - for speed you're better using libraries like gmpy. Just replace your calls to float() to gmpy.mpq() and your code should now give exact results (though you may want to format the results as floats for display purposes).

正如其他人所提到的,python 2.6 将有一个内置的理性类型,但请注意,这并不是真正的高性能实现 - 为了速度,您最好使用像gmpy这样的。只需将您对 float() 的调用替换为 gmpy.mpq() ,您的代码现在应该会给出准确的结果(尽管您可能希望将结果格式化为浮点数以进行显示)。

Here's a slightly tidied version of your code to load a matrix that will use gmpy rationals instead:

这是您的代码的稍微整理版本,用于加载将使用 gmpy 有理数的矩阵:

def read_matrix(f):
    b,y = [], []
    for line in f:
        bits = line.split(",")
        b.append( map(gmpy.mpq, bits[:-1]) )
        y.append(gmpy.mpq(bits[-1]))
    return b,y

回答by jfs

It is not an answer to your question, but related:

这不是您问题的答案,而是相关的:

#!/usr/bin/env python
from numpy import abs, dot, loadtxt, max
from numpy.linalg import solve

data = loadtxt('gauss.dat', delimiter=',')
a, b = data[:,:-1], data[:,-1:]
x = solve(a, b) # here you may use any method you like instead of `solve`
print(x)
print(max(abs((dot(a, x) - b) / b))) # check solution

Example:

例子:

$ cat gauss.dat
4.0, 2.0, 1.0, 11.0
1.0, 5.0, 3.0, 6.0 
2.0, 2.0, 5.0, 7.0

$ python loadtxt_example.py
[[ 2.4]
 [ 0.6]
 [ 0.2]]
0.0

回答by Matthew Schinckel

Also see What is a simple example of floating point error, here on SO, which has some answers. The one I give actually uses python as the example language...

另请参阅什么是浮点错误的简单示例,这里有一些答案。我给出的实际上使用 python 作为示例语言......