Python多精度有理数比较:Fraction、mpq和mpfr

Python multi-precision rational comparison: Fraction, mpq and mpfr

我了解浮点计算因其性质而并不准确。我正在尝试找出最好的 library/way 来进行多精度定量比较。我正在比较 Fraction、mpq 和 mpfr。后两个来自 gmpy2 库。第一个来自 fractions 包。我正在使用 python3.3

这是我用来比较的脚本。写的不是很好,很简单的一篇

from fractions import Fraction
from gmpy2 import mpq, mpfr
import time

# This script compares gmpy2 library and Fraction library

total_pass_mpq = 0
total_pass_mpfr = 0
total_pass_frc = 0

a = mpq("-3.232429")
a_ = Fraction("-3.232429")
a__ = mpfr("-3.232429")
if str(float(a)) == "-3.232429":
    total_pass_mpq +=1
if str(float(a_)) == "-3.232429":
    total_pass_frc += 1
if str(float(a__)) == "-3.232429":
    total_pass_mpfr += 1

b = mpq("604.08")
c = mpq("1.979")
b_ = Fraction("604.08")
c_ = Fraction("1.979")
b__ = mpfr("604.08")
c__ = mpfr("1.979")
if str(float(b*c)) == "1195.47432":
    total_pass_mpq += 1
if str(float(b_*c_)) == "1195.47432":
    total_pass_frc += 1
if str(float(b__*c__)) == "1195.47432":
    total_pass_mpfr += 1

d = mpq(604.08)
e = mpq(1.979)
d_ = Fraction(604.08)
e_ = Fraction(1.979)
d__ = mpfr(604.08)
e__ = mpfr(1.979)
if str(float(d*e)) == "1195.47432":
    total_pass_mpq += 1
if str(float(d_*e_)) == "1195.47432":
    total_pass_frc += 1
if str(float(d__*e__)) == "1195.47432":
    total_pass_mpfr += 1

f = mpq(-3.232429)
f_ = Fraction(-3.232429)
f__ = mpfr(-3.232429)
if str(float(f)) == "-3.232429":
    total_pass_mpq +=1
if str(float(f_)) == "-3.232429":
    total_pass_frc += 1
if str(float(f__)) == "-3.232429":
    total_pass_mpfr +=1

g = mpq(503.79)
g_ = Fraction(503.79)
g__ = mpfr(503.79)
h = mpq(0.07)
h_ = Fraction(0.07)
h__ = mpfr(0.07)
if str(float(g*(1+h))) == "539.0553":
    total_pass_mpq += 1
if str(float(g_*(1+h_))) == "539.0553":
    total_pass_frc += 1
if str(float(g__*(1+h__))) == "539.0553":
    total_pass_mpfr += 1

print("Total passed mpq: " + str(total_pass_mpq))
print("Total passed Fraction: " + str(total_pass_frc))
print("Total passed mpfr: " + str(total_pass_mpfr))

start_mpq = time.time()
for i in range(0, 50000):
    y = mpq(0.32329)
    z = mpq(-1)
    yz = y*z
end_mpq = time.time()
print("Time for executing mpq: " + str(end_mpq - start_mpq))

start_frc = time.time()
for j in range(0, 50000):
    y = Fraction(0.32329)
    z = Fraction(-1)
    yz_ = y*z
end_frc = time.time()
print("Time for executing frc: " + str(end_frc - start_frc))

start_frc_2 = time.time()
for j_ in range(0, 50000):
    y = Fraction(0.32329)
    z = Fraction(-1)
    yz_2 = y*z
end_frc_2 = time.time()
print("Time for executing frc str: " + str(end_frc_2 - start_frc_2))

start_mpfr = time.time()
for k in range(0, 50000):
    y = mpfr(0.32329)
    z = mpfr(-1)
    yz__ = y*z
end_mpfr = time.time()
print("Time for executing mpfr: " + str(end_mpfr - start_mpfr))

start_mpfr_2 = time.time()
for k_ in range(0, 50000):
    y = mpfr("0.32329")
    z = mpfr("-1")
    yz__2 = y*z
end_mpfr_2 = time.time()
print("Time for executing mpfr str: " + str(end_mpfr_2 - start_mpfr_2))

这是结果:

Total passed mpq: 3
Total passed Fraction: 5
Total passed mpfr: 4
Time for executing mpq: 0.04700875282287598
Time for executing frc: 2.1327619552612305
Time for executing frc str: 2.0934295654296875
Time for executing mpfr: 0.05441713333129883
Time for executing mpfr str: 0.12844634056091309

所以基本上我得到的结果是 Fraction 是最准确的,但是它超级慢。对于这个问题,我想问一下,

  1. 您认为我还应该尝试其他情况吗?
  2. 还有其他图书馆吗?
  3. 如果速度很重要,有没有办法使用 gmpy2 库提高精度?

float(mpq) 调用 GMP 库函数 mpq_get_q。我检查了 GMP 源,mpq_get_d 将中间结果四舍五入为 0。它没有计算出正确的四舍五入结果。 (在这种情况下,正确四舍五入意味着四舍五入到最接近偶数。)所以它偶尔会不同于 float(Fraction).

GMP 库未针对浮点计算进行优化。要获得正确舍入的浮动值,您应该使用 MFPR 库(又名 gmpy2 中的 mpfr 类型)。

mpq 转换为 float 的最准确方法是先将其转换为 mpfr。为避免双舍入,您应该从 mpq 转换为 mpfr,精度为 53 位。所以float(mpfr(mpq, 53))。 (默认精度当前为 53 位,但将来可能会更改。建议指定所需的精度,或确保将默认上下文的精度设置为 53。)此更改使 mpqFraction return 您的示例中的结果相同。

还有一个 mpfr 结果不同。这只是中间 mpfr 计算四舍五入到当前精度(在本例中为 53 位)这一事实所固有的。

更新以回答@mattsun 的问题。

为什么 mpfr("503.79")*(mpfr("1")+mpfr("0.07")) 不等于“539.0553”?

Python 的 float 类型和 gmpy2 的 mpfr 类型都使用二进制或基数 2 表示。当我们处理数字时,我们通常使用十进制或基数 10 表示。就像 1/3 不能用十进制算术精确表示一样,大多数十进制数不能用二进制表示法精确表示。计算是使用接近但不完全等于给定值的值完成的。错误会累积,结果会与您的预期值略有不同。

有两种选择:

1) 将字符串格式化为所需的十进制格式。

2) 使用 decimal 库。

免责声明:我维护gmpy2