#python #fractions #mpfr #gmpy
#python #дроби #mpfr #gmpy
Вопрос:
Я понимаю, что вычисление с плавающей запятой не является точным из-за его природы. Я пытаюсь найти лучшую библиотеку / способ сделать многоточное сравнение рационов. Я сравниваю фракцию, 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.
mpq
иFraction
должна быть равной (фактически бесконечной) точностью, поскольку они оба хранят произвольную точностьint
s в качестве числителя и знаменателя. Я подозреваю, что ваши тесты плохо разработаны (слишком полагаются на представление с плавающей запятой), если они утверждают, что у них нет точности соответствия.mpq
в основном должна быть более быстрая версияFraction
, вот и все. Однако в обоих случаях инициализация из afloat
вызывает проблемы;float
у s есть проблемы с представлением, которые разные типы рациональных чисел могут преобразовывать по-разному.2. Например, @ShadowRanger
float(mpq("-3.232429"))
дает мне-3.2324289999999998
, покаfloat(Fraction("-3.232429"))
дает мне-3.232429
. Как вы думаете, это ожидаемо?3. Вы предполагаете, что целью библиотек является обработка математики с плавающей запятой. Это не так. Как только вы покидаете область рациональных чисел, библиотеки выходят за рамки предполагаемого варианта использования. Я не знаю точно, где возникает «ошибка», но обратное преобразование из
mpq
вfloat
может отличаться из-за слишком большой точности в GMP (где ошибка с плавающей запятой зависит от точности; может помочь преобразовать себяmympq.numerator / mympq.denominator
, чтобы это сделал Python), или слишком мало. Если вы конвертируете в и изfloat
, вы неправильно выполняете математику рациональных чисел.4. @ShadowRanger Как насчет этого:
>>> from gmpy2 import sub >>> sub(mpq("-3.232429"), mpq(-3.232429)) >>> mpq(7697,35184372088832000000)
почему оно не равно нулю? Разве это не неточно?5. Это верно и для
Fraction
too, потому что, как я уже сказал, ВЫ НЕ МОЖЕТЕ ИСПОЛЬЗОВАТЬfloat
С БИБЛИОТЕКАМИ РАЦИОНАЛЬНЫХ ЧИСЕЛ! ЭТО НЕ ТО, ДЛЯ ЧЕГО ОНИ ПРЕДНАЗНАЧЕНЫ! Один из них становится дробью-3232429/1000000
, другой становится-7278783019950793/2251799813685248
, потому что это фактическое соотношение, представленноеfloat
значением -3.232429 . Строка является точной, и обаFraction
иmpq
использовать его, чтобы получить точное представление «разделить на степень 10». Afloat
будет преобразовываться менее интуитивно, потому что вы даете им число, которое на самом деле не является рациональным, как вы думаете.
Ответ №1:
float(mpq)
вызывает библиотечную функцию GMP mpq_get_q
. Я проверил источник GMP и mpq_get_d
округлил промежуточный результат до 0. Он не вычисляет правильно округленный результат. (В этом случае правильное округление подразумевает округление до ближайшего с привязкой к четному.) Так что иногда это будет отличаться от float(Fraction)
.
Библиотека GMP не оптимизирована для вычислений с плавающей запятой. Чтобы получить правильно округленные значения с плавающей запятой, вы должны использовать библиотеку MFPR (она же mpfr
type in gmpy2
).
Самый точный способ преобразовать an mpq
в a float
— сначала преобразовать его в an 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
тип Python, так и mpfr
тип gmpy2 используют двоичное, или radix-2, представление. Обычно мы используем десятичное или десятичное представление, когда работаем с числами. Точно так же, как 1/3
cannon может быть точно представлено в десятичной арифметике, большинство десятичных чисел не могут быть точно представлены в двоичном представлении. Вычисления выполняются со значениями, которые близки, но не точно равны заданным значениям. Ошибки могут накапливаться, и результат будет немного отличаться от вашего ожидаемого значения.
Есть два варианта:
1) Отформатируйте строку в желаемый десятичный формат.
2) Используйте decimal
библиотеку.
Отказ от ответственности: Я поддерживаю gmpy2
.
Комментарии:
1. Спасибо за ответ. Я не уверен, что правильно понял это. Учитывая это выражение
mpfr("503.79")*(mpfr("1") mpfr("0.07"))
, оно дает мне результатmpfr('539.0553000000001')
. Тем не менее, я хотел получить значение «539.0553». Как?2. Независимо от приведенного выше примера, то, что вы сказали, преобразование mpq в mpfr (53 бит) имеет смысл и полезно.