Python的浮点任意精度可用?

只是为了好玩,因为这很简单,我写了一个简短的程序来生成嫁接数字 ,但是由于浮点精度问题,没有find一些更大的例子。

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 

这段代码错过了至less一个已知的嫁接号码。 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 

所以我写了一个简短的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; } 

哪些产出:

 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或其他?

在标准库中, decimal模块可能是你正在寻找的。 另外,我发现mpmath是相当有帮助的。 该文档有很多很好的例子(不幸的是,我的办公室电脑没有安装mpmath ,否则我会validation一些例子并发布)。

虽然, decimal模块的一个警告。 该模块包含了一些用于简单math运算(例如sqrt )的内置函数,但这些函数的结果可能并不总是与math或其他模块中的相应函数在更高的精度(尽pipe它们可能更准确)相匹配。 例如,

 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中,输出前两行

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

如上所述,这不正是你所期望的,你可以看到精度越高,结果匹配的越less。 请注意,在这个例子中, decimal模块的精度更高,因为它与实际值更接近。

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

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

既然你正在寻找一个十进制表示最自然的属性,那么使用二进制表示就有点傻了。 您链接到的维基百科页面没有指出在“嫁接数字”开始之前可能会出现多less“非嫁接数字”,因此您可以指定:

 >>> 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()的结果很可能会比二进制表示和十进制表示之间的转换更准确,至less是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') 

您可以尝试使用十进制而不是浮点数。

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