浮点任意精度是否可用?

Is floating point arbitrary precision available?

提问人:OmnipotentEntity 提问时间:7/17/2012 最后编辑:Benjamin LoisonOmnipotentEntity 更新时间:7/31/2023 访问量:81158

问:

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

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

此代码至少遗漏了一个已知的嫁接编号。 乘以 后,它似乎会降低额外的精度。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 或其他东西?

Python 点浮 点精度

评论

3赞 jfs 9/30/2015
要对有理数进行精确的算术运算,可以使用分数模块

答:

9赞 f p 7/17/2012 #1

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

6赞 Ned Batchelder 7/17/2012 #2

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

66赞 Ricardo Altamirano 7/17/2012 #3

在标准库中,decimal 模块可能就是您要查找的模块。此外,我发现 mpmath 非常有帮助。文档中还有很多很好的例子(不幸的是,我的办公室电脑没有安装;否则我会验证一些例子并发布它们)。mpmath

不过,关于 decimal 模块的一个警告。该模块包含几个用于简单数学运算的内置函数(例如),但这些函数的结果可能并不总是与更高精度的相应函数或其他模块匹配(尽管它们可能更准确)。例如sqrtmath

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

如前所述,这并不完全是您所期望的,您可以看到精度越高,结果匹配度越低。请注意,在此示例中,该模块确实具有更高的准确性,因为它与实际值更接近。decimal

评论

5赞 DSM 7/17/2012
+1 代表 。使用十进制数的问题在于,你不能在十进制对象上做太多数学函数,所以如果你只是在玩,这是非常有限的。mpmath
4赞 senderle 7/17/2012
需要明确的是,我很确定,在你对 vs. 的测试中,由于二进制到十进制的转换,产生的结果不太正确。考虑 的输出与 的输出。math.sqrtDecimal.sqrt()math.sqrtdecimal.Decimal(math.sqrt(num) ** 2) * 7decimal.Decimal(num.sqrt() ** 2) * 7
5赞 OmnipotentEntity 7/17/2012
考虑到sqrt(1/7)的实际值,十进制sqrt函数似乎更准确。0.377964473009227227214516536234180060815751311868921454338333494171581260461469089680056126639220515802...
7赞 DSM 7/17/2012
而不是 ,你只是想要 . 从低精度数学函数生成 Decimal 对象,而不是执行高精度 sqrt。Decimal(math.sqrt(num))num.sqrt()Decimal(math.sqrt(num))
5赞 nonopolarity 1/8/2016
嗯......如果“实际值”实际上比这更长,我认为你不能把它写成“实际值”
11赞 senderle 7/17/2012 #4

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

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

由于您正在寻找以十进制表示法最自然地表示的属性,因此使用二进制表示形式有点愚蠢。您链接到的维基百科页面没有指出在“移植数字”开始之前可能出现多少个“非嫁接数字”,因此您可以指定:

>>> 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()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')
3赞 U13-Forward 9/18/2018 #5

使用 ,(这里有一个更清晰的例子):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赞 Smart Manoj 3/19/2022
十进制('0.1') !=十进制(0.1)
0赞 Jürgen A. Erhard 5/22/2023
@SmartManoj当我刚刚在 3.11.2、3.10.11 和 2.7 中尝试时返回 False。
0赞 Smart Manoj 5/23/2023
@JürgenA.艾哈德·imgur.com/a/oTGnwec
0赞 Jürgen A. Erhard 6/3/2023
哦,我把 Decimal(0.1) 读成 Decimal('0.1'),我的错,对不起。