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 。