decimal --- 十進位固定點和浮點運算

原始碼:Lib/decimal.py


decimal 模組提供對快速且正確捨入的十進位浮點運算的支援。它提供了優於 float 資料型別的幾項優勢:

  • Decimal 「是基於一個浮點模型,該模型在設計時考量了人類的需求,並且必然有一個最重要的指導原則——電腦必須提供一種與人們在學校學到的算術相同運作方式的算術。」——摘自十進位算術規格。

  • Decimal 數字可以被精確表示。相對地,像 1.12.2 這樣的數字在二進位浮點數中沒有精確的表示方式。終端使用者通常不會期望 1.1 + 2.2 顯示為 3.3000000000000003,但這正是二進位浮點數的表現。

  • 精確性延續到算術運算中。在十進位浮點數中,0.1 + 0.1 + 0.1 - 0.3 完全等於零。在二進位浮點數中,結果是 5.5511151231257827e-017。雖然接近零,但這些差異會妨礙可靠的相等性測試,並且差異可能累積。因此,在具有嚴格相等不變量的會計應用程式中,decimal 是首選。

  • decimal 模組包含了有效位數的概念,因此 1.30 + 1.20 等於 2.50。尾隨零被保留以表示有效性。這是金融應用程式的慣用呈現方式。對於乘法,「教科書」方法使用被乘數中的所有數字。例如,1.3 * 1.2 得到 1.56,而 1.30 * 1.20 得到 1.5600

  • 與基於硬體的二進位浮點數不同,decimal 模組具有使用者可變的精度(預設為 28 位),可以根據給定問題的需要設定得足夠大:

    >>> from decimal import *
    >>> getcontext().prec = 6
    >>> Decimal(1) / Decimal(7)
    Decimal('0.142857')
    >>> getcontext().prec = 28
    >>> Decimal(1) / Decimal(7)
    Decimal('0.1428571428571428571428571429')
    
  • 二進位和十進位浮點數都是根據已發布的標準實作的。雖然內建的 float 型別只暴露其功能的一小部分,但 decimal 模組暴露了標準的所有必要部分。必要時,程式設計師對捨入和訊號處理有完全的控制權。這包括透過使用例外來阻止任何不精確運算以強制執行精確算術的選項。

  • decimal 模組被設計為支援「不帶偏見地同時支援精確無捨入的十進位算術(有時稱為固定點算術)和有捨入的浮點算術。」——摘自十進位算術規格。

模組設計圍繞三個概念:decimal 數字、算術情境和訊號。

decimal 數字是不可變的。它有符號、係數數字和指數。為了保持有效性,係數數字不會截斷尾隨零。Decimal 也包含特殊值,如 Infinity-InfinityNaN。標準也區分 -0+0

算術情境是一個指定精度、捨入規則、指數限制、指示運算結果的旗標,以及決定訊號是否被視為例外的陷阱啟用器的環境。捨入選項包括 ROUND_CEILINGROUND_DOWNROUND_FLOORROUND_HALF_DOWNROUND_HALF_EVENROUND_HALF_UPROUND_UPROUND_05UP

訊號是在計算過程中產生的例外條件群組。根據應用程式的需求,訊號可能被忽略、視為資訊性質,或被視為例外。decimal 模組中的訊號包括:ClampedInvalidOperationDivisionByZeroInexactRoundedSubnormalOverflowUnderflowFloatOperation

每個訊號都有一個旗標和一個陷阱啟用器。當遇到訊號時,其旗標被設定為一,然後,如果陷阱啟用器被設定為一,就會引發例外。旗標是黏性的,因此使用者需要在監控計算之前重設它們。

也參考

快速入門教學

使用 decimal 的通常起始步驟是引入模組、使用 getcontext() 檢視目前的情境,以及若有必要的話,設定精度、捨入方式或啟用陷阱的新值:

>>> from decimal import *
>>> getcontext()
Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999,
        capitals=1, clamp=0, flags=[], traps=[Overflow, DivisionByZero,
        InvalidOperation])

>>> getcontext().prec = 7       # 設定新的精度

Decimal 實例可以從整數、字串、浮點數或 tuple 建構。從整數或浮點數建構會對該整數或浮點數的值進行精確轉換。Decimal 數字包括特殊值,例如代表「非數字」的 NaN、正負 Infinity-0::

>>> getcontext().prec = 28
>>> Decimal(10)
Decimal('10')
>>> Decimal('3.14')
Decimal('3.14')
>>> Decimal(3.14)
Decimal('3.140000000000000124344978758017532527446746826171875')
>>> Decimal((0, (3, 1, 4), -2))
Decimal('3.14')
>>> Decimal(str(2.0 ** 0.5))
Decimal('1.4142135623730951')
>>> Decimal(2) ** Decimal('0.5')
Decimal('1.414213562373095048801688724')
>>> Decimal('NaN')
Decimal('NaN')
>>> Decimal('-Infinity')
Decimal('-Infinity')

如果 FloatOperation 訊號被捕捉,在建構函式或排序比較中意外混用 decimal 和 float 會引發例外:

>>> c = getcontext()
>>> c.traps[FloatOperation] = True
>>> Decimal(3.14)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
decimal.FloatOperation: [<class 'decimal.FloatOperation'>]
>>> Decimal('3.5') < 3.7
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
decimal.FloatOperation: [<class 'decimal.FloatOperation'>]
>>> Decimal('3.5') == 3.5
True

在 3.3 版被加入.

新 Decimal 的精度僅由輸入的數字位數決定。情境精度和捨入只會在算術運算期間發揮作用。

>>> getcontext().prec = 6
>>> Decimal('3.0')
Decimal('3.0')
>>> Decimal('3.1415926535')
Decimal('3.1415926535')