2016-10-18 47 views
0

我知道浮點計算由於其性質而不準確。我試圖找出最好的庫/方式來進行多精度定量比較。我在比較分數,mpq和mpfr。後兩者來自gmpy2庫。第一個來自分數包。我正在使用python3.3Python多精度合理比較:分數,mpq和mpfr

這是我用來比較的腳本。寫得不好,很簡單。

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庫的精度?
+0

'mpq'和'Fraction'應該是相等的(實際上是無限的)精度,因爲它們都存儲任意的精度'int's作爲分子和分母。如果他們聲稱他們兩個沒有匹配的精度,我懷疑你的測試設計得不好(依靠浮點表示太多)。 'mpq'應該基本上是'Fraction'的更快版本,就是這樣。不過,在這兩種情況下,從「浮動」初始化都是要求麻煩的; '浮動'有代表性的問題,不同的有理數字類型可能會有不同的轉換。 – ShadowRanger

+0

@ShadowRanger例如,'float(mpq(「 - 3.232429」))''給我'-3.2324289999999998'而'float(Fraction(「 - 3.232429」))'給我'-3.232429'。你認爲這是預期的嗎? – mattsun

+0

你假設圖書館的目標是處理浮點數學。不是。只要你離開理性數字領域,圖書館就不在他們預期的用例範圍內。我不清楚「錯誤」究竟在哪裏蔓延,但從「mpq」返回到「float」的轉換可能會因爲GMP中的精度太高而不同(其中浮點錯誤隨精度而變化;可能有助於轉換自己'mympq.numerator/mympq.denominator'讓Python做到這一點),而不是太少。如果你正在從「float」轉換,並且你沒有正確地進行有理數的數學運算。 – ShadowRanger

回答

1

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在您的示例中返回相同的結果。

還有一個mpfr的結果是不同的。這是中間的mpfr計算正在四捨五入到當前精度(在這種情況下是53位)的事實。

更新回答@mattsun提問。

爲什麼mpfr("503.79")*(mpfr("1")+mpfr("0.07"))不等於「539.0553」?

Python的float類型和gmpy2的mpfr類型使用二進制或基數爲2的表示形式。當我們使用數字時,我們通常使用十進制或十進制表示。就像1/3大炮完全用十進制算術表示的那樣,大多數小數不能用二進制表示完全表示。這些計算是使用與給定值相近但不完全相等的值完成的。錯誤可能會累積,結果會與您的期望值稍有不同。

有兩個選項:

1)格式的字符串所期望的十進制格式。

2)使用decimal庫。

聲明:我保留gmpy2

+0

謝謝你的回答。我不確定我是否正確理解這一點。給出這個表達式,'mpfr(「503.79」)*(mpfr(「1」)+ mpfr(「0.07」))',它給了我結果'mpfr('539.0553000000001')'。但是,我想獲得價值「539.0553」。怎麼樣? – mattsun

+0

不管上面的例子,你說的話,把mpq轉換成mpfr(53bit)是有意義的,並且是有用的。 – mattsun