Python多精度理性比较:分数,mpq和mpfr

时间:2016-10-18 04:17:28

标签: python fractions mpfr gmpy

据我所知,由于其性质,浮点计算不准确。我正在努力寻找最佳的库/方式来进行多精度定量比较。我正在比较Fraction,mpq和mpfr。后两者来自gmpy2库。第一个是来自分数包。我正在使用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库提高精度?

1 个答案:

答案 0 :(得分:1)

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

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

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

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

更新以回答@mattsun提出的问题。

为什么mpfr不等于" 539.0553"?

Python的mpfr("503.79")*(mpfr("1")+mpfr("0.07"))类型和gmpy2' float类型都使用二进制或基数-2表示。当我们处理数字时,我们通常使用decimal或radix-10表示。就像mpfr加农炮精确地用十进制算术表示一样,大多数十进制数不能用二进制表示精确表示。使用与给定值接近但不完全相等的值进行计算。错误可能会累积,结果与预期值略有不同。

有两种选择:

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

2)使用1/3库。

免责声明:我保留decimal