Рациональное сравнение с несколькими точками Python: Fraction, mpq и mpfr
Я понимаю, что вычисление с плавающей точкой не является точным из-за его природы. Я пытаюсь найти лучшую библиотеку / способ сделать мульти-точность сравнения рационов. Я сравниваю фракцию, 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
В общем, я получил результат, что фракция самая точная, но она очень медленная. На этот вопрос я хотел спросить,
- Есть ли другой случай, который вы думаете, я должен попробовать?
- любая другая библиотека?
- Если скорость имеет значение, есть ли способ повысить точность, используя библиотеку gmpy2?
1 ответ
float(mpq)
вызывает функцию библиотеки GMP mpq_get_q
, Я проверил источник GMP и mpq_get_d
округляет промежуточный результат до 0. Он не вычисляет правильно округленный результат. (В этом случае правильно округленный подразумевает округление до ближайшего с привязкой к четному.) Так что иногда оно будет отличаться от float(Fraction)
,
Библиотека GMP не оптимизирована для вычислений с плавающей запятой. Чтобы получить правильно округленные плавающие значения, вы должны использовать MFPR libary (он же mpfr
введите gmpy2
).
Самый точный способ конвертировать mpq
к float
чтобы преобразовать его в mpfr
первый. Чтобы избежать двойного округления, вы должны конвертировать из mpq
в mpfr
с точностью ровно 53 бита. Так float(mpfr(mpq, 53))
, (Точность по умолчанию в настоящее время составляет 53 бита, но она может измениться в будущем. Рекомендуется указывать желаемую точность или обеспечивать точность контекста по умолчанию, равную 53). Это изменение делает mpq
а также Fraction
верните те же результаты в вашем примере.
Есть еще один mpfr
результат, который отличается. Это просто присуще тому, что промежуточный mpfr
вычисления округляются до текущей точности (в данном случае 53 бита).
Обновление, чтобы ответить на вопрос @mattsun.
Почему mpfr("503.79")*(mpfr("1")+mpfr("0.07"))
не равно "539.0553"?
Оба питона float
тип и gmpy2 mpfr
тип использует двоичное представление, или radix-2, представление. Обычно мы используем десятичное, или radix-10, представление, когда работаем с числами. Как 1/3
нельзя представить точно в десятичной арифметике, большинство десятичных чисел не может быть представлено точно в двоичном представлении. Расчеты выполняются со значениями, которые близки, но не совсем равны приведенным значениям. Ошибки могут накапливаться, и результат будет немного отличаться от вашего ожидаемого значения.
Есть два варианта:
1) Отформатируйте строку в желаемый десятичный формат.
2) Используйте decimal
библиотека.
Отказ от ответственности: я утверждаю gmpy2
,