web-dev-qa-db-ja.com

Python浮動小数点の任意精度が利用可能ですか?

楽しみのために、そしてそれが本当に簡単だったので、 Grafting numbers を生成する短いプログラムを書きましたが、浮動小数点の精度の問題のため、大きな例のいくつかを見つけていません。

def isGrafting(a):
  for i in xrange(1, int(ceil(log10(a))) + 2):
    if a == floor((sqrt(a) * 10**(i-1)) % 10**int(ceil(log10(a)))):
      return 1

a = 0
while(1):
  if (isGrafting(a)):
    print "%d %.15f" % (a, sqrt(a))
  a += 1

このコードは、少なくとも1つの既知のグラフティング番号を逃しています。 9999999998 => 99999.99998999999999949999999994999999999374999999912...10**5で乗算した後、余分な精度を落とすようです。

>>> a = 9999999998
>>> sqrt(a)
99999.99999
>>> a == floor((sqrt(a) * 10**(5)) % 10**int(ceil(log10(a))))
False
>>> floor((sqrt(a) * 10**(5)) % 10**int(ceil(log10(a))))
9999999999.0
>>> print "%.15f" % sqrt(a)
99999.999989999996615
>>> print "%.15f" % (sqrt(a) * 10**5)
9999999999.000000000000000

CPUが浮動小数点数を切り捨てたのか、それともpython.

#include <cstdio>
#include <cmath>
#include <stdint.h>

int main()
{
  uint64_t a = 9999999998;
  printf("%ld %.15f %.15f %.15f %.15f\n", a, sqrt((double)a), sqrt((double)a)*1e4, sqrt((double)a)*1e5, sqrt((double)a)*1e6);
  a = 999999999998;
  printf("%ld %.15f %.15f %.15f %.15f\n", a, sqrt((double)a), sqrt((double)a)*1e5, sqrt((double)a)*1e6, sqrt((double)a)*1e7);
  a = 99999999999998;
  printf("%ld %.15f %.15f %.15f %.15f\n", a, sqrt((double)a), sqrt((double)a)*1e6, sqrt((double)a)*1e7, sqrt((double)a)*1e8);
  return 0;
}

どの出力:

9999999998 99999.999989999996615 999999999.899999976158142 9999999999.000000000000000 99999999990.000000000000000
999999999998 999999.999998999992386 99999999999.899993896484375 999999999999.000000000000000 9999999999990.000000000000000
99999999999998 9999999.999999899417162 9999999999999.900390625000000 99999999999999.000000000000000 999999999999990.000000000000000

だから、浮動小数点の精度の限界に逆らって実行しているように見え、CPUは残りのビットを切り捨てています。なぜなら、残りの違いは浮動小数点エラーだと考えているからです。 Pythonの下でこれを回避する方法はありますか?または、Cに移動してGMPなどを使用する必要がありますか?

41

標準ライブラリでは、 decimal モジュールが探しているものです。また、 mpmath が非常に役立つことがわかりました。 documentation にも多くの素晴らしい例があります(残念ながら私のオフィスのコンピューターにはmpmathがインストールされていません;そうでなければ、いくつかの例を検証して投稿します)。

ただし、 decimal モジュールに関する注意点が1つあります。モジュールには、単純な数学演算用のいくつかの組み込み関数(たとえば、sqrt)が含まれていますが、これらの関数の結果は、mathまたはその他のモジュールの対応する関数と必ずしも一致しない場合があります(ただし、より正確な場合があります)。例えば、

from decimal import *
import math

getcontext().prec = 30
num = Decimal(1) / Decimal(7)

print("   math.sqrt: {0}".format(Decimal(math.sqrt(num))))
print("decimal.sqrt: {0}".format(num.sqrt()))

Python 3.2.3では、これは最初の2行を出力します

   math.sqrt: 0.37796447300922719758631274089566431939601898193359375
decimal.sqrt: 0.377964473009227227214516536234
actual value: 0.3779644730092272272145165362341800608157513118689214

述べたように、これは期待したとおりではなく、精度が高いほど、結果が一致しないことがわかります。 decimalモジュールは 実際の値 により厳密に一致するため、この例ではより正確です。

39

この特定の問題の場合、decimalは10進数字をタプルとして格納するため、最適な方法です。

_>>> a = decimal.Decimal(9999999998)
>>> a.as_Tuple()
DecimalTuple(sign=0, digits=(9, 9, 9, 9, 9, 9, 9, 9, 9, 8), exponent=0)
_

10進表記で最も自然に表現されるプロパティを探しているので、バイナリ表現を使用するのは少しばかげています。リンクしたウィキペディアのページには、「接ぎ木桁」が始まる前に表示される「非接ぎ木桁」の数が示されていなかったため、以下を指定できます。

_>>> def isGrafting(dec, max_offset=5):
...     dec_digits = dec.as_Tuple().digits
...     sqrt_digits = dec.sqrt().as_Tuple().digits
...     windows = [sqrt_digits[o:o + len(dec_digits)] for o in range(max_offset)]
...     return dec_digits in windows
... 
>>> isGrafting(decimal.Decimal(9999999998))
True
>>> isGrafting(decimal.Decimal(77))
True
_

少なくとも、このためには、Decimal.sqrt()の結果が、2進表現と10進表現の間の変換のため、math.sqrt()の結果よりも正確になる可能性が高いと思います。たとえば、次のことを考慮してください。

_>>> num = decimal.Decimal(1) / decimal.Decimal(7)
>>> decimal.Decimal(math.sqrt(num) ** 2) * 7
Decimal('0.9999999999999997501998194593')
>>> decimal.Decimal(num.sqrt() ** 2) * 7
Decimal('1.000000000000000000000000000')
_
8
senderle

浮動小数点の代わりに Decimal で試すことができます。

7
f p

Pythonには任意精度の浮動小数点が組み込まれていませんが、サードパーティPython GMPを使用するパッケージ: gmpy および PyGMP があります。

5
Ned Batchelder

decimalを使用します(より明確な例です):

>>> 2.3-2.2
0.09999999999999964
>>> from decimal import Decimal
>>> Decimal('2.3')-Decimal('2.2')
Decimal('0.1')
>>> float(Decimal('2.3')-Decimal('2.2'))
0.1
>>> 
0
U10-Forward