web-dev-qa-db-ja.com

PythonのDecimal型の明確化

だれでも、少なくとも すべてのプログラマが知っておくべきこと は、float型を使用すると精度エラーが発生する可能性があることを知っています。ただし、場合によっては、正確な解決策が適切であり、イプシロン値を使用して比較するだけでは不十分な場合があります。とにかく、それが本当の意味ではありません。

PythonのDecimalタイプについて知っていましたが、これを使用しようとしたことはありません。 "10進数は正確に表すことができます" であり、それは、実数を表すことができる賢い実装を意味するということでした。

>>> from decimal import Decimal
>>> d = Decimal(1) / Decimal(3)
>>> d3 = d * Decimal(3)
>>> d3 < Decimal(1)
True

かなりがっかりしたので、ドキュメントに戻って読み続けました。

算術のコンテキストは、精度を指定する環境です[...]

はい、実際には精度があります。そして古典的な問題を再現することができます:

>>> dd = d * 10**20
>>> dd
Decimal('33333333333333333333.33333333')
>>> for i in range(10000):
...    dd += 1 / Decimal(10**10)
>>> dd
Decimal('33333333333333333333.33333333')

したがって、私の質問は次のとおりです:無限精度のDecimal型を使用する方法はありますか?そうでない場合、2つの10進数を比較するよりエレガントな方法は何ですか(たとえば、デルタが精度よりも小さい場合、d3 <1はFalseを返す必要があります)。

現在、除算と乗算のみを行う場合は、Fractionタイプを使用します。

>>> from fractions import Fraction
>>> f = Fraction(1) / Fraction(3)
>>> f
Fraction(1, 3)
>>> f * 3 < 1
False
>>> f * 3 == 1
True

それは最善のアプローチですか?他のオプションは何でしょうか?

35
Maxime Chéramy

Decimalクラスは、財務タイプの加算、減算、乗算、除算タイプの問題に最適です。

>>> (1.1+2.2-3.3)*10000000000000000000
4440.892098500626                            # relevant for government invoices...
>>> import decimal
>>> D=decimal.Decimal
>>> (D('1.1')+D('2.2')-D('3.3'))*10000000000000000000
Decimal('0.0')

Fractionモジュールは、あなたが説明する有理数の問題領域でうまく機能します:

>>> from fractions import Fraction
>>> f = Fraction(1) / Fraction(3)
>>> f
Fraction(1, 3)
>>> f * 3 < 1
False
>>> f * 3 == 1
True

科学研究用の純粋な多精度浮動小数点については、 mpmath を検討してください。

問題がシンボリック領域に留まる可能性がある場合は、 sympy を検討してください。以下は、1/3の問題の処理方法です。

>>> sympy.sympify('1/3')*3
1
>>> (sympy.sympify('1/3')*3) == 1
True

Sympyは、任意の精度の浮動小数点にmpmathを使用し、有理数と無理数を記号的に処理する機能を備えています。

√2の無理値の純粋な浮動小数点表現を考えます。

>>> math.sqrt(2)
1.4142135623730951
>>> math.sqrt(2)*math.sqrt(2)
2.0000000000000004
>>> math.sqrt(2)*math.sqrt(2)==2
False

Sympyと比較:

>>> sympy.sqrt(2)
sqrt(2)                              # treated symbolically
>>> sympy.sqrt(2)*sympy.sqrt(2)==2
True

値を減らすこともできます。

>>> import sympy
>>> sympy.sqrt(8)
2*sqrt(2)                            # √8 == √(4 x 2) == 2*√2...

ただし、注意しないと、Sympyで直線浮動小数点に類似した問題が発生することがあります。

>>> 1.1+2.2-3.3
4.440892098500626e-16
>>> sympy.sympify('1.1+2.2-3.3')
4.44089209850063e-16                   # :-(

これはDecimalで行う方が良いでしょう:

>>> D('1.1')+D('2.2')-D('3.3')
Decimal('0.0')

または、分数またはSympyを使用して、1.1などの値を比率として保持します。

>>> sympy.sympify('11/10+22/10-33/10')==0
True
>>> Fraction('1.1')+Fraction('2.2')-Fraction('3.3')==0
True

または、sympyでRationalを使用します。

>>> frac=sympy.Rational
>>> frac('1.1')+frac('2.2')-frac('3.3')==0
True
>>> frac('1/3')*3
1

sympy live でプレイできます。

47
dawg

だから、私の質問は:無限の精度でDecimal型を作成する方法はありますか?

いいえ、無理数を保存するには無限のメモリが必要になるためです。

Decimalが役立つのは、金額などが正確である必要があり、精度がアプリオリにわかっている場合などです。

質問から、Decimalfloatよりもユースケースに適しているかどうかは完全には明らかではありません。

4
NPE

精度が無限のDecimal型を作成する方法はありますか?

番号;実際の行の空でない間隔では、有限のビット数を使用して、セット内のすべての数値を無限の精度で表すことはできません。これがFractionが有用である理由です。これは、分子と分母を整数として保存し、canを正確に表すためです。

>>> Fraction("1.25")
Fraction(5, 4)
3
jonrsharpe

Decimalが初めての場合は、この投稿が関連します。 Python浮動小数点の任意の精度を利用できますか?

回答とコメントからの本質的なアイデアは、精度が必要な計算上困難な問題の場合、mpmathモジュールを使用する必要があるということです https://code.google.com/p/mpmath/ =。重要な観察は、

Decimal数値を使用する場合の問題は、Decimalオブジェクトの数学関数では多くのことを実行できないことです。

1
William Denman