2010-11-15 76 views
16

我有試圖處理與4位小數精度數大量的Python代碼,我堅持使用Python 2.4的原因有很多。該代碼做了非常簡單的數學運算(其信用管理代碼大多需要或添加積分)惡蟒十進制/浮

它混合使用float和Decimal(MySQLdb爲SQL DECIMAL類型返回Decimal對象)。在使用過程中出現了一些奇怪的錯誤之後,我發現所有的根本原因都是代碼中浮點數和小數點進行比較的幾個地方。

我得到的情況是這樣的:

>>> from decimal import Decimal 
>>> max(Decimal('0.06'), 0.6) 
Decimal("0.06") 

現在我擔心的是,我可能無法趕上所有此類案件中的代碼。 (一個普通的程序員將繼續做x> 0而不是x>十進制('0.0000'),這是很難避免)

我想出了一個補丁(靈感來自python 2.7中十進制包的改進) 。

import decimal 
def _convert_other(other): 
    """Convert other to Decimal. 

    Verifies that it's ok to use in an implicit construction. 
    """ 
    if isinstance(other, Decimal): 
     return other 
    if isinstance(other, (int, long)): 
     return Decimal(other) 
    # Our small patch begins 
    if isinstance(other, float): 
     return Decimal(str(other)) 
    # Our small patch ends 
    return NotImplemented 
decimal._convert_other = _convert_other 

我只是做了一個非常早期的裝載庫,它會通過允許浮動到十進制轉換比較之前(以避免撞到到對象比較Python的默認對象)將十進制包行爲。

,因爲它修復了一些浮法的四捨五入情況下,我專門用於「海峽」,而不是「再版」。例如。

>>> Decimal(str(0.6)) 
Decimal("0.6") 
>>> Decimal(repr(0.6)) 
Decimal("0.59999999999999998") 

現在我的問題是: 我錯過了什麼嗎?這相當安全嗎?或者我在這裏打破了什麼? (我想到了包的作者有很強的理由,以避免花車這麼多)

回答

4

我想你想raise NotImplementedError(),而不是return NotImplemented,開始。

你在做什麼叫做「猴子補丁」,只要你知道你在做什麼,意識到這個後果,並且沒有問題。通常情況下,您將此限制爲修復錯誤,或者您知道對行爲進行更改的其他更改仍然正確並向後兼容。

在這種情況下,因爲你貼敷類,你可以在那裏你使用它的情況下,外部改變的行爲。如果另一個庫使用十進制,並以某種方式依賴於默認行爲,它可能會導致微妙的錯誤。麻煩的是,除非您審覈全部您的代碼,包括任何依賴關係,並查找所有呼叫站點,否則您並不真正知道。

基本上 - 做你自己的風險。

個人而言,我覺得它更讓人放心的解決我的所有代碼,添加測試,並使其更難做錯誤的事情(例如,使用包裝類或輔助功能)。另一種方法是用你的補丁程序代碼來找到所有的呼叫站點,然後返回並修復它們。

編輯 - 我想我應該補充說,他們避免花車可能的原因是浮動不能準確地表示所有的數字,如果你與金錢打交道這是非常重要的。

+1

只需注意「return NotImplemented」來自decimal.py包本身。我添加的兩條線是在評論之間。然而,我同意你的方法,在這個實現中,python允許在我們假設都是數字的對象之間進行邏輯上瘋狂的比較。嗯,另一個想法可能是提出一個錯誤,而不是隱式轉換,但無論如何,我認爲我需要做點什麼... – 2010-11-15 08:30:57

+10

'return NotImplemented'是正確的,並且是正確的,[documentation specified](http:// docs .python.org/reference/datamodel.html#emulating-numeric-types)返回一個不支持的比較。它允許Python試圖找到另一種做事的方式。 – aaronasterling 2010-11-15 08:35:48

+0

使用術語「猴子修補」+1,這導致我的wikipedia這個詞,找到它來自「游擊隊補丁」,就像在游擊戰爭=)。 – Tommy 2013-10-17 03:11:20

3

有很好的理由來避免浮動。使用浮點數時,由於浮點噪聲,無法可靠地進行比較,如==,>,<等。任何浮點運算都會累積噪聲。它開始於非常小的數字出現在最後,例如1.000 ... 002,但最終可能會累積,例如1.0000000453436。

如果你沒有做那麼多的浮點計算,使用str()可能會適合你,但是如果你做了很多計算,浮點噪聲最終會足夠大,str()會給你錯誤的答案。

總之,如果 (1)你不這樣做,許多浮點運算,或 (2)你不需要做比較喜歡==>,<等 ,那麼你可能是好的。

如果你想確定,那麼刪除所有的浮點代碼。

+0

有很好的理由可以避免在會計程序**中出現類似於問題中的浮動**。對於代表**近似**數量的預期目的,浮動工作得很好。 – dan04 2010-11-15 17:11:31

+1

@丹,是的,我回答的前提是你不能用浮游物做==。如果您表示近似數量,那麼您不使用==,因爲相等並不是近似值。 – 2010-11-15 19:24:44