Рациональное сравнение с несколькими точками 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

В общем, я получил результат, что фракция самая точная, но она очень медленная. На этот вопрос я хотел спросить,

  1. Есть ли другой случай, который вы думаете, я должен попробовать?
  2. любая другая библиотека?
  3. Если скорость имеет значение, есть ли способ повысить точность, используя библиотеку 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,

Другие вопросы по тегам