Python浮点任意精度可用吗?

Omn*_*ity 42 python floating-point floating-accuracy

只是为了好玩,因为它非常简单,我编写了一个简短的程序来生成嫁接数,但由于浮点精度问题,它没有找到一些更大的例子.

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
Run Code Online (Sandbox Code Playgroud)

此代码缺少至少一个已知的嫁接编号.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
Run Code Online (Sandbox Code Playgroud)

所以我写了一个简短的C++程序,看看是不是我的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;
}
Run Code Online (Sandbox Code Playgroud)

哪个输出:

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
Run Code Online (Sandbox Code Playgroud)

所以看起来我正在努力克服浮点精度的限制,并且CPU正在切断剩余的位,因为它认为剩下的差异是浮点错误.有没有办法在Python下解决这个问题?或者我是否需要转移到C并使用GMP或其他什么?

Ric*_*ano 41

在标准库中,decimal模块可能是您正在寻找的.另外,我发现mpmath非常有帮助.该文档也有很多很好的例子(不幸的是我的办公室计算机没有mpmath安装;否则我会验证一些例子并发布它们).

decimal但是,关于该模块的一个警告.该模块包含几个用于简单数学运算的内置函数(例如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()))
Run Code Online (Sandbox Code Playgroud)

在Python 3.2.3中,它输出前两行

   math.sqrt: 0.37796447300922719758631274089566431939601898193359375
decimal.sqrt: 0.377964473009227227214516536234
actual value: 0.3779644730092272272145165362341800608157513118689214
Run Code Online (Sandbox Code Playgroud)

如上所述,并不完全符合您的预期,您可以看到精度越高,结果越匹配.请注意,decimal在此示例中,模块确实具有更高的准确性,因为它更接近实际值.

  • 你只需要`num.sqrt()`而不是`Decimal(math.sqrt(num))`.`Decimal(math.sqrt(num))`从低精度数学函数构建一个Decimal对象,而不是做一个高精度的sqrt. (6认同)
  • 考虑到sqrt(1/7)的实际值是`0.377964473009227227214516536234180060815751311868921454338333494171581260461469089680056126639220515802 ...`似乎小数sqrt函数更准确. (4认同)
  • +1为'mpmath`.使用十进制数字的问题在于你在十进制对象上的数学函数方式上做不了多少,所以如果你只是在玩它就非常有限. (3认同)
  • 嗯...如果“实际价值”实际上比“实际价值”长,我认为您不能将其写为“实际价值” (3认同)
  • 请明确说明-我相当确定在对`math.sqrt`和Decimal.sqrt()进行的测试中,由于二进制转换为-十进制转换。考虑`decimal.Decimal(math.sqrt(num)** 2)* 7`的输出,而不是`decimal.Decimal(num.sqrt()** 2)* 7`的输出。 (2认同)

sen*_*rle 8

对于这个特殊问题,这decimal是一个很好的方法,因为它将十进制数字存储为元组!

>>> a = decimal.Decimal(9999999998)
>>> a.as_tuple()
DecimalTuple(sign=0, digits=(9, 9, 9, 9, 9, 9, 9, 9, 9, 8), exponent=0)
Run Code Online (Sandbox Code Playgroud)

由于您正在寻找以十进制表示法自然表达的属性,因此使用二进制表示法有点愚蠢.您链接到的维基百科页面并未指示在"嫁接数字"开始之前可能出现多少"非嫁接数字",因此您可以指定:

>>> 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
Run Code Online (Sandbox Code Playgroud)

我认为Decimal.sqrt(),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')
Run Code Online (Sandbox Code Playgroud)


f p*_*f p 7

您可以尝试使用Decimal而不是浮点数.


Ned*_*der 5

Python没有内置的任意精度浮点数,但是有第三方Python包使用GMP: gmpyPyGMP.