2008-11-13 141 views
2

我似乎失去了很多使用浮点数的精度。小数位浮点数和小数点的位置问题。小数点

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

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

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

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

,我需要解决使用高斯 - 赛德尔所以我需要重新排列x,y和z的方程:

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

这里是我用来重新排列equ ations。 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 

但是我找回答案并不精确到小数点后一位。

例如,在重新安排从上面的第二个方程,我应该得到:

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

我得到的是:

y=-1.2-0.20000000000000001x+0.59999999999999998z 

这看起来可能不是一个大问题,但是当你提高数量非常高,功率误差相当大。有没有解决的办法?我尝试了Decimal课程,但它不适用于权力(即Decimal(x)**2)。

任何想法?

回答

11

我不熟悉Decimal类可以帮助你,但是你的问题是由于小数不能用二进制精确表示的事实,所以你看到的是最接近可能的近似值;如果不使用特殊的类(如Decimal,可能),则无法避免此问题。

EDIT:小数班怎么样对您来说工作不正常?只要我从一个字符串开始,而不是一个浮动,权力似乎正常工作。

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

module documentation解释了必要性和decimal.Decimal使用非常清楚,你应该检查出来,如果你还没有。

+0

我以前用力量的问题是我试图提高到0.5的能力。为此,我必须编写decimal.Decimal(“1.2)** decimal.Decimal(”0.5“) – darudude 2008-11-13 15:39:47

+0

您错过了一个引用,也许这就是问题所在。如果您发现它太冗长,可以始终使用D = decimal.Decimal; D(“1.2”)** D(“0.5”) – recursive 2009-01-02 17:09:28

14

IEEE浮点是二进制的,而不是十进制的。没有固定长度的二进制分数恰好为0.1或其任何倍数。它是一个重复分数,如十进制的1/3。

请仔细阅读What Every Computer Scientist Should Know About Floating-Point Arithmetic

其他选项除了一个小数类使用的Common Lisp或Python 2.6或确切有理数

  • 转换双打另一种语言使用关闭有理数,例如

  • +0

    @Doug:我添加了对Python 2.6及其分数模块的引用 – tzot 2008-11-13 08:59:24

    4

    首先,您的输入可以被简化很多。您不需要读取和解析文件。你可以用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 ] 
    

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

    你可以试试Python 2.6 fractions模块。有一个旧的rational包可能会有所帮助。

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

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

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

    我会对这样的任务小心模块。它的目的实际上更多地处理实际的十进制数(例如,匹配人类簿记实践),具有有限精度,而不执行精确的精确数学。有数字不完全可以用十进制表示,就像二进制数字一样,用十进制表示算术也比替代方法慢得多。

    相反,如果你想得到确切的结果,你应该使用有理算术。这些将数字表示为分子/去噪对,因此可以精确地表示所有有理数。如果你只使用乘法和除法(而不是平方根等可能导致不合理数字的操作),则不会失去精度。

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

    这里是你的代码稍微收拾版本加载,将使用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 
    
    2

    这不是一个回答你的问题,但相关:

    #!/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 
    

    例:

    $ 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 
    
    0

    只是一个建议(我不知道你在什么限制下工作):

    为什么不使用直接的高斯消元而不是高斯 - 赛德尔迭代?如果您选择具有最大值的系数作为每个淘汰步骤的支点,则可以最小化FP舍入误差。

    这可能实际上是由J.F. Sebastian(!!)提到的numpy.linalg.solve所做的。

    相关问题