浮動小数点数と小数の小数点位置の問題
-
08-07-2019 - |
質問
フロートでは精度が大幅に低下しているようです。
たとえば、行列を解く必要があります:
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]))
gauss-seidelを使用して解く必要があるため、x、y、およびzの方程式を再配置する必要があります。
x=(11+2y-1z)/4
y=(-6-x+3z)/5
z=(7-2x-2y)/7
方程式を再配置するために使用するコードを次に示します。 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
ただし、返される回答は小数点以下の精度ではありません。
たとえば、上記の2番目の式を並べ替えると、次のようになります。
y=-1.2-.2x+.6z
私が得るのは:
y=-1.2-0.20000000000000001x+0.59999999999999998z
これは大きな問題とは思えないかもしれませんが、数値を非常に高い電力に上げると、エラーは非常に大きくなります。これを回避する方法はありますか? Decimal
クラスを試しましたが、パワー(つまり、 Decimal(x)** 2
)ではうまく機能しません。
アイデアはありますか
解決
Decimalクラスについて十分な知識がありませんが、問題は小数が2進数で正確に表現できないことが多いためです。したがって、表示されるのは可能な限り最も近い近似です。特別なクラス(おそらくDecimalなど)を使用せずにこの問題を回避する方法はありません。
EDIT:
decimalクラスが適切に機能していないのはどうですか?フロートではなく文字列で開始する限り、パワーは正常に機能するようです。
>>> import decimal
>>> print(decimal.Decimal("1.2") ** 2)
1.44
モジュールドキュメントは、 decimal.Decimalの必要性と使用法を説明しています。
かなり明確に、まだ行っていない場合はチェックアウトする必要があります。
他のヒント
IEEE浮動小数点は10進数ではなく2進数です。正確に0.1またはその倍数である固定長のバイナリ分数はありません。これは、小数の1/3のような繰り返しの分数です。
すべてのコンピューター科学者が浮動小数点演算について知っておくべきこと
Decimalクラス以外のその他のオプションは
-
Common Lispまたは Python 2.6 を使用または正確な理論的根拠を持つ別の言語
最初に、入力を大幅に簡素化できます。ファイルを読み込んで解析する必要はありません。 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 ]
2番目、y = -1.2-0.20000000000000001x + 0.59999999999999998zは異常ではありません。 0.2または0.6のバイナリ表記では正確な表現はありません。したがって、表示される値は、正確な表現ではなく元の10進数の近似値です。これらは、存在するあらゆる種類の浮動小数点プロセッサに当てはまります。
Python 2.6 fractions モジュールを試すことができます。古い合理的パッケージが役立つ場合があります。
はい、浮動小数点数を累乗するとエラーが増加します。そのため、浮動小数点数の右端の位置を使用しないように注意する必要があります。これらのビットはほとんどノイズであるためです。
浮動小数点数を表示するときは、ノイズビットが見えないように適切に丸める必要があります。
>>> a
0.20000000000000001
>>> "%.4f" % (a,)
'0.2000'
このようなタスクでは、decimalモジュールに注意してください。その目的は、実際の正確な計算を実行せずに、実際の10進数(たとえば、人間の簿記慣行に一致する)を有限精度で処理することです。 2進数のように10進数で正確に表現できない数値もあり、10進数での算術の実行も他の方法よりはるかに遅くなります。
代わりに、正確な結果が必要な場合は、有理数演算を使用する必要があります。これらは、分子/デノメンターのペアとして数値を表すため、すべての有理数を正確に表すことができます。 (無理な数になる平方根のような演算ではなく)乗算と除算のみを使用している場合、精度が失われることはありません。
他の人が述べたように、Python 2.6には組み込みの合理的な型がありますが、これは実際には高性能な実装ではないことに注意してください-速度のために、 gmpy 。 float()の呼び出しをgmpy.mpq()に置き換えるだけで、コードで正確な結果が得られるはずです(ただし、表示目的で結果をfloatとしてフォーマットすることもできます)。
これは、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
これはあなたの質問に対する答えではなく、関連しています:
#!/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
浮動小数点エラーの簡単な例もご覧ください。 、ここSOにはいくつかの答えがあります。私が実際に提供するものは、Pythonをサンプル言語として使用しています...