decimal — 十進位制定點和浮點算術

原始碼: Lib/decimal.py


decimal 模組提供了對快速、正確舍入的十進位制浮點算術的支援。它比 float 資料型別有幾個優點

  • 十進位制數“基於一個為人們設計的浮點模型,並且必然有一個最重要的指導原則——計算機必須提供一種與人們在學校學習的算術運算方式相同的方式。” – 摘自十進位制算術規範。

  • 十進位制數可以精確表示。相比之下,像 1.12.2 這樣的數字在二進位制浮點數中沒有精確的表示。終端使用者通常不會期望 1.1 + 2.2 像二進位制浮點數那樣顯示為 3.3000000000000003

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

  • decimal 模組包含有效位數的概念,因此 1.30 + 1.202.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 模組旨在“無偏見地同時支援精確的未舍入十進位制算術(有時稱為定點算術)和舍入的浮點算術。” – 摘自十進位制算術規範。

該模組的設計圍繞三個概念:十進位制數、算術上下文和訊號。

十進位制數是不可變的。它具有符號、係數數字和指數。為了保留有效性,係數數字不會截斷尾隨零。十進位制數還包括特殊值,例如 Infinity-InfinityNaN。該標準還將 -0+0 區分開。

算術上下文是一個指定精度、舍入規則、指數限制、指示運算結果的標誌和確定訊號是否被視為異常的陷阱使能器的環境。舍入選項包括 ROUND_CEILINGROUND_DOWNROUND_FLOORROUND_HALF_DOWNROUND_HALF_EVENROUND_HALF_UPROUND_UPROUND_05UP

訊號是計算過程中出現的一組異常情況。根據應用程式的需要,訊號可以被忽略、被視為資訊或被視為異常。decimal 模組中的訊號為:ClampedInvalidOperationDivisionByZeroInexactRoundedSubnormalOverflowUnderflowFloatOperation

對於每個訊號,都有一個標誌和一個陷阱使能器。當遇到訊號時,其標誌設定為 1,然後,如果陷阱使能器設定為 1,則會引發異常。標誌是粘性的,因此使用者需要在監視計算之前重置它們。

參見

快速入門教程

使用十進位制數的通常開始是匯入模組,使用 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       # Set a new precision

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 訊號被捕獲,則在建構函式或排序比較中意外混合十進位制數和浮點數會引發異常

>>> 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')
>>> Decimal('3.1415926535') + Decimal('2.7182818285')
Decimal('5.85987')
>>> getcontext().rounding = ROUND_UP
>>> Decimal('3.1415926535') + Decimal('2.7182818285')
Decimal('5.85988')

如果超出 C 版本的內部限制,構造一個 decimal 會引發 InvalidOperation 異常。

>>> Decimal("1e9999999999999999999")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
decimal.InvalidOperation: [<class 'decimal.InvalidOperation'>]

在 3.3 版本中更改。

Decimal 可以很好地與 Python 的其他部分互動。以下是一個關於 decimal 浮點數的簡單演示:

>>> data = list(map(Decimal, '1.34 1.87 3.45 2.35 1.00 0.03 9.25'.split()))
>>> max(data)
Decimal('9.25')
>>> min(data)
Decimal('0.03')
>>> sorted(data)
[Decimal('0.03'), Decimal('1.00'), Decimal('1.34'), Decimal('1.87'),
 Decimal('2.35'), Decimal('3.45'), Decimal('9.25')]
>>> sum(data)
Decimal('19.29')
>>> a,b,c = data[:3]
>>> str(a)
'1.34'
>>> float(a)
1.34
>>> round(a, 1)
Decimal('1.3')
>>> int(a)
1
>>> a * 5
Decimal('6.70')
>>> a * b
Decimal('2.5058')
>>> c % a
Decimal('0.77')

一些數學函式也可用於 Decimal:

>>> getcontext().prec = 28
>>> Decimal(2).sqrt()
Decimal('1.414213562373095048801688724')
>>> Decimal(1).exp()
Decimal('2.718281828459045235360287471')
>>> Decimal('10').ln()
Decimal('2.302585092994045684017991455')
>>> Decimal('10').log10()
Decimal('1')

quantize() 方法將數字四捨五入到固定指數。此方法對於貨幣應用非常有用,貨幣應用通常將結果四捨五入到固定的小數位數。

>>> Decimal('7.325').quantize(Decimal('.01'), rounding=ROUND_DOWN)
Decimal('7.32')
>>> Decimal('7.325').quantize(Decimal('1.'), rounding=ROUND_UP)
Decimal('8')

如上所示,getcontext() 函式訪問當前上下文,並允許更改設定。此方法滿足大多數應用程式的需求。

對於更高階的工作,使用 Context() 建構函式建立備用上下文可能會很有用。要使備用上下文生效,請使用 setcontext() 函式。

根據標準,decimal 模組提供了兩個可立即使用的標準上下文,BasicContextExtendedContext。前者對於除錯特別有用,因為許多陷阱已啟用。

>>> myothercontext = Context(prec=60, rounding=ROUND_HALF_DOWN)
>>> setcontext(myothercontext)
>>> Decimal(1) / Decimal(7)
Decimal('0.142857142857142857142857142857142857142857142857142857142857')

>>> ExtendedContext
Context(prec=9, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999,
        capitals=1, clamp=0, flags=[], traps=[])
>>> setcontext(ExtendedContext)
>>> Decimal(1) / Decimal(7)
Decimal('0.142857143')
>>> Decimal(42) / Decimal(0)
Decimal('Infinity')

>>> setcontext(BasicContext)
>>> Decimal(42) / Decimal(0)
Traceback (most recent call last):
  File "<pyshell#143>", line 1, in -toplevel-
    Decimal(42) / Decimal(0)
DivisionByZero: x / 0

上下文還具有訊號標誌,用於監視計算期間遇到的異常情況。這些標誌會一直保持設定狀態,直到被顯式清除,因此最好在使用 clear_flags() 方法之前,在每次進行受監視的計算之前清除這些標誌。

>>> setcontext(ExtendedContext)
>>> getcontext().clear_flags()
>>> Decimal(355) / Decimal(113)
Decimal('3.14159292')
>>> getcontext()
Context(prec=9, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999,
        capitals=1, clamp=0, flags=[Inexact, Rounded], traps=[])

flags 條目顯示,pi 的有理數近似值被舍入(超出上下文精度的數字被丟棄),並且結果不精確(一些被丟棄的數字為非零)。

可以使用上下文的 traps 屬性中的字典來設定各個陷阱。

>>> setcontext(ExtendedContext)
>>> Decimal(1) / Decimal(0)
Decimal('Infinity')
>>> getcontext().traps[DivisionByZero] = 1
>>> Decimal(1) / Decimal(0)
Traceback (most recent call last):
  File "<pyshell#112>", line 1, in -toplevel-
    Decimal(1) / Decimal(0)
DivisionByZero: x / 0

大多數程式僅在程式開始時調整當前上下文一次。並且在許多應用程式中,資料在迴圈內使用單個型別轉換轉換為 Decimal。設定好上下文並建立了 decimal 後,程式的大部分操作資料的方式與使用其他 Python 數字型別沒有區別。

Decimal 物件

class decimal.Decimal(value='0', context=None)

基於 *value* 構造一個新的 Decimal 物件。

value 可以是整數、字串、元組、float 或另一個 Decimal 物件。 如果沒有給出 *value*,則返回 Decimal('0')。如果 *value* 是字串,則在刪除開頭和結尾的空白字元以及所有下劃線後,它應符合 decimal 數字字串語法。

sign           ::=  '+' | '-'
digit          ::=  '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
indicator      ::=  'e' | 'E'
digits         ::=  digit [digit]...
decimal-part   ::=  digits '.' [digits] | ['.'] digits
exponent-part  ::=  indicator [sign] digits
infinity       ::=  'Infinity' | 'Inf'
nan            ::=  'NaN' [digits] | 'sNaN' [digits]
numeric-value  ::=  decimal-part [exponent-part] | infinity
numeric-string ::=  [sign] numeric-value | [sign] nan

在上面的 digit 出現的地方,也允許使用其他 Unicode 十進位制數字。這些包括來自各種其他字母表(例如,阿拉伯-印度和提婆那伽梨數字)的十進位制數字以及全形數字 '\uff10''\uff19'

如果 *value* 是一個 tuple,它應具有三個組成部分,一個符號(0 表示正數,或 1 表示負數),一個數字的 tuple 和一個整數指數。例如,Decimal((0, (1, 4, 1, 4), -3)) 返回 Decimal('1.414')

如果 *value* 是一個 float,則二進位制浮點值將無損轉換為其精確的十進位制等效項。此轉換通常可能需要 53 位或更多位的精度。例如,Decimal(float('1.1')) 轉換為 Decimal('1.100000000000000088817841970012523233890533447265625')

context 精度不會影響儲存的位數。它完全由 *value* 中的位數確定。例如,即使上下文精度只有三位,Decimal('3.00000') 也會記錄所有五個零。

context 引數的目的是確定如果 *value* 是格式錯誤的字串該怎麼做。如果上下文捕獲 InvalidOperation,則會引發異常;否則,建構函式將返回一個新的值為 NaN 的 Decimal。

構造完成後,Decimal 物件是不可變的。

在 3.2 版本中更改: 現在允許建構函式的引數是 float 例項。

在 3.3 版本中更改: 如果設定了 FloatOperation 陷阱,則 float 引數將引發異常。預設情況下,該陷阱處於關閉狀態。

在 3.6 版本中更改: 允許使用下劃線進行分組,就像程式碼中的整數和浮點文字一樣。

Decimal 浮點數物件與諸如 floatint 之類的其他內建數字型別共享許多屬性。所有常用的數學運算和特殊方法都適用。同樣,decimal 物件可以被複制、pickle、列印、用作字典鍵、用作集合元素、比較、排序以及強制轉換為另一種型別(例如 floatint)。

在 Decimal 物件上進行算術運算與在整數和浮點數上進行算術運算之間存在一些細微的差異。當餘數運算子 % 應用於 Decimal 物件時,結果的符號是被除數的符號,而不是除數的符號。

>>> (-7) % 4
1
>>> Decimal(-7) % Decimal(4)
Decimal('-3')

整數除法運算子 // 的行為類似,返回真商的整數部分(向零截斷)而不是其向下取整,以便保留通常的恆等式 x == (x // y) * y + x % y

>>> -7 // 4
-2
>>> Decimal(-7) // Decimal(4)
Decimal('-1')

%// 運算子分別實現規範中描述的 remainderdivide-integer 操作。

十進位制物件通常不能與浮點數或 fractions.Fraction 的例項進行算術運算:例如,嘗試將 Decimal 加到 float 將引發 TypeError。但是,可以使用 Python 的比較運算子將 Decimal 例項 x 與另一個數字 y 進行比較。這可以避免在比較不同型別的數字時產生混淆的結果。

在 3.2 版本中更改: 現在完全支援 Decimal 例項與其他數字型別之間的混合型別比較。

除了標準的數字屬性外,十進位制浮點數物件還具有許多專用方法。

adjusted()

返回調整後的指數,方法是移出係數的最右邊的數字,直到只剩下前導數字: Decimal('321e+5').adjusted() 返回 7。用於確定相對於小數點的最高有效數字的位置。

as_integer_ratio()

返回一對整數 (n, d),它們將給定的 Decimal 例項表示為分數,該分數以最簡形式且具有正分母。

>>> Decimal('-3.14').as_integer_ratio()
(-157, 50)

轉換是精確的。對無窮大引發 OverflowError,對 NaN 引發 ValueError。

在 3.6 版本中新增。

as_tuple()

返回數字的 命名元組 表示形式:DecimalTuple(sign, digits, exponent)

canonical()

返回引數的規範編碼。目前, Decimal 例項的編碼始終是規範的,因此此操作返回其未更改的引數。

compare(other, context=None)

比較兩個 Decimal 例項的值。 compare() 返回一個 Decimal 例項,如果其中一個運算元是 NaN,則結果是 NaN。

a or b is a NaN  ==> Decimal('NaN')
a < b            ==> Decimal('-1')
a == b           ==> Decimal('0')
a > b            ==> Decimal('1')
compare_signal(other, context=None)

此操作與 compare() 方法相同,只是所有 NaN 都會發出訊號。也就是說,如果兩個運算元都不是 signaling NaN,則任何 quiet NaN 運算元都會被視為 signaling NaN。

compare_total(other, context=None)

使用運算元的抽象表示而不是數值來比較兩個運算元。與 compare() 方法類似,但是結果給出了 Decimal 例項的完全排序。在此排序中,具有相同數值但表示不同的兩個 Decimal 例項會比較為不相等。

>>> Decimal('12.0').compare_total(Decimal('12'))
Decimal('-1')

quiet 和 signaling NaN 也包括在完全排序中。如果兩個運算元具有相同的表示形式,則此函式的結果為 Decimal('0');如果第一個運算元在總順序中低於第二個運算元,則結果為 Decimal('-1');如果第一個運算元在總順序中高於第二個運算元,則結果為 Decimal('1')。有關總順序的詳細資訊,請參閱規範。

此操作不受上下文影響,並且是安靜的:不會更改任何標誌,也不會執行任何舍入。作為例外,如果無法精確轉換第二個運算元,則 C 版本可能會引發 InvalidOperation。

compare_total_mag(other, context=None)

使用運算元的抽象表示而不是其值來比較兩個運算元,如 compare_total() 中所示,但忽略每個運算元的符號。x.compare_total_mag(y) 等效於 x.copy_abs().compare_total(y.copy_abs())

此操作不受上下文影響,並且是安靜的:不會更改任何標誌,也不會執行任何舍入。作為例外,如果無法精確轉換第二個運算元,則 C 版本可能會引發 InvalidOperation。

conjugate()

只返回 self,此方法僅是為了符合十進位制規範。

copy_abs()

返回引數的絕對值。此操作不受上下文影響,並且是安靜的:不會更改任何標誌,也不會執行任何舍入。

copy_negate()

返回引數的負值。此操作不受上下文影響,並且是安靜的:不會更改任何標誌,也不會執行任何舍入。

copy_sign(other, context=None)

返回第一個運算元的副本,其符號設定為與第二個運算元的符號相同。例如

>>> Decimal('2.3').copy_sign(Decimal('-1.5'))
Decimal('-2.3')

此操作不受上下文影響,並且是安靜的:不會更改任何標誌,也不會執行任何舍入。作為例外,如果無法精確轉換第二個運算元,則 C 版本可能會引發 InvalidOperation。

exp(context=None)

返回給定數字的(自然)指數函式 e**x 的值。使用 ROUND_HALF_EVEN 舍入模式正確舍入結果。

>>> Decimal(1).exp()
Decimal('2.718281828459045235360287471')
>>> Decimal(321).exp()
Decimal('2.561702493119680037517373933E+139')
classmethod from_float(f)

僅接受 floatint 例項的替代建構函式。

請注意,Decimal.from_float(0.1)Decimal('0.1') 不同。由於 0.1 在二進位制浮點數中不能精確表示,因此該值儲存為最接近的可表示值,即 0x1.999999999999ap-4。十進位制的等效值為 0.1000000000000000055511151231257827021181583404541015625

注意

從 Python 3.2 開始,Decimal 例項也可以直接從 float 構建。

>>> Decimal.from_float(0.1)
Decimal('0.1000000000000000055511151231257827021181583404541015625')
>>> Decimal.from_float(float('nan'))
Decimal('NaN')
>>> Decimal.from_float(float('inf'))
Decimal('Infinity')
>>> Decimal.from_float(float('-inf'))
Decimal('-Infinity')

3.1 版本新增。

fma(other, third, context=None)

融合乘加運算。返回 self*other+third,不進行中間乘積 self*other 的舍入。

>>> Decimal(2).fma(3, 5)
Decimal('11')
is_canonical()

如果引數是規範的,則返回 True,否則返回 False。目前,Decimal 例項始終是規範的,因此此操作始終返回 True

is_finite()

如果引數是有限數,則返回 True,如果引數是無窮大或 NaN,則返回 False

is_infinite()

如果引數是正無窮大或負無窮大,則返回 True,否則返回 False

is_nan()

如果引數是(靜默或信令)NaN,則返回 True,否則返回 False

is_normal(context=None)

如果引數是正常的有限數,則返回 True。如果引數是零、次正規數、無窮大或 NaN,則返回 False

is_qnan()

如果引數是靜默 NaN,則返回 True,否則返回 False

is_signed()

如果引數具有負號,則返回 True,否則返回 False。請注意,零和 NaN 都可以帶有符號。

is_snan()

如果引數是信令 NaN,則返回 True,否則返回 False

is_subnormal(context=None)

如果引數是次正規數,則返回 True,否則返回 False

is_zero()

如果引數是(正或負)零,則返回 True,否則返回 False

ln(context=None)

返回運算元的自然對數(底數為 e)。結果使用 ROUND_HALF_EVEN 舍入模式進行正確舍入。

log10(context=None)

返回運算元的以 10 為底的對數。結果使用 ROUND_HALF_EVEN 舍入模式進行正確舍入。

logb(context=None)

對於非零數字,將其運算元的調整後的指數作為 Decimal 例項返回。如果運算元是零,則返回 Decimal('-Infinity') 並引發 DivisionByZero 標誌。如果運算元是無窮大,則返回 Decimal('Infinity')

logical_and(other, context=None)

logical_and() 是一種邏輯運算,它接受兩個邏輯運算元(參見 邏輯運算元)。結果是兩個運算元的逐位 and

logical_invert(context=None)

logical_invert() 是一種邏輯運算。結果是運算元的逐位反轉。

logical_or(other, context=None)

logical_or() 是一種邏輯運算,它接受兩個邏輯運算元(參見 邏輯運算元)。結果是兩個運算元的逐位 or

logical_xor(other, context=None)

logical_xor() 是一種邏輯運算,它接受兩個邏輯運算元(參見 邏輯運算元)。結果是兩個運算元的逐位異或。

max(other, context=None)

類似於 max(self, other),不同之處在於在返回之前應用上下文舍入規則,並且 NaN 值會被髮出訊號或忽略(取決於上下文以及它們是信令還是靜默)。

max_mag(other, context=None)

max() 方法類似,但比較是使用運算元的絕對值進行的。

min(other, context=None)

類似於 min(self, other),不同之處在於,返回之前會應用上下文舍入規則,並且 NaN 值要麼被髮出訊號,要麼被忽略(取決於上下文以及它們是發出訊號的 NaN 還是安靜的 NaN)。

min_mag(other, context=None)

min() 方法類似,但比較是使用運算元的絕對值進行的。

next_minus(context=None)

返回給定上下文中(如果未給出上下文,則返回當前執行緒的上下文中)可表示的、小於給定運算元的最大數字。

next_plus(context=None)

返回給定上下文中(如果未給出上下文,則返回當前執行緒的上下文中)可表示的、大於給定運算元的最小數字。

next_toward(other, context=None)

如果兩個運算元不相等,則返回最接近第一個運算元,並且方向指向第二個運算元的數字。 如果兩個運算元在數值上相等,則返回第一個運算元的副本,並將符號設定為與第二個運算元的符號相同。

normalize(context=None)

用於在當前上下文或指定的上下文內生成等價類的規範值。

這與一元加運算具有相同的語義,但如果最終結果是有限的,則會將其簡化為最簡單的形式,刪除所有尾隨零並保留其符號。 也就是說,當係數非零且是 10 的倍數時,係數除以 10,並且指數增加 1。 否則(係數為零),指數設定為 0。在所有情況下,符號保持不變。

例如,Decimal('32.100')Decimal('0.321000e+2') 都被規範化為等效值 Decimal('32.1')

請注意,舍入是在簡化為最簡形式之前應用的。

在最新版本的規範中,此操作也稱為 reduce

number_class(context=None)

返回一個描述運算元的字串。 返回的值是以下十個字串之一。

  • "-Infinity",表示運算元為負無窮大。

  • "-Normal",表示運算元為負正規數。

  • "-Subnormal",表示運算元為負次正規數。

  • "-Zero",表示運算元為負零。

  • "+Zero",表示運算元為正零。

  • "+Subnormal",表示運算元為正次正規數。

  • "+Normal",表示運算元為正正規數。

  • "+Infinity",表示運算元為正無窮大。

  • "NaN",表示運算元是安靜的 NaN(非數字)。

  • "sNaN",表示運算元是發出訊號的 NaN。

quantize(exp, rounding=None, context=None)

返回一個等於第一個運算元舍入後,並且具有第二個運算元的指數的值。

>>> Decimal('1.41421356').quantize(Decimal('1.000'))
Decimal('1.414')

與其他操作不同,如果量化操作後係數的長度大於精度,則會發出 InvalidOperation 訊號。 這保證了,除非存在錯誤情況,否則量化的指數始終等於右手運算元的指數。

同樣與其他操作不同,即使結果是次正規數且不精確,量化也永遠不會發出下溢訊號。

如果第二個運算元的指數大於第一個運算元的指數,則可能需要舍入。 在這種情況下,舍入模式由給定的 rounding 引數確定(如果給定),否則由給定的 context 引數確定; 如果兩個引數均未給出,則使用當前執行緒上下文的舍入模式。

只要結果指數大於 Emax 或小於 Etiny(),就會返回錯誤。

radix()

返回 Decimal(10),即 Decimal 類執行所有算術的基數(底數)。 為了與規範相容而包含。

remainder_near(other, context=None)

返回 self 除以 other 的餘數。 這與 self % other 的不同之處在於,選擇餘數的符號是為了最小化其絕對值。 更準確地說,返回值是 self - n * other,其中 n 是最接近 self / other 精確值的整數,並且如果兩個整數同樣接近,則選擇偶數。

如果結果為零,則其符號將為 self 的符號。

>>> Decimal(18).remainder_near(Decimal(10))
Decimal('-2')
>>> Decimal(25).remainder_near(Decimal(10))
Decimal('5')
>>> Decimal(35).remainder_near(Decimal(10))
Decimal('-5')
rotate(other, context=None)

返回將第一個運算元的數字旋轉由第二個運算元指定的值的結果。 第二個運算元必須是 -precision 到 precision 範圍內的整數。 第二個運算元的絕對值給出了旋轉的位數。 如果第二個運算元為正,則向左旋轉;否則,向右旋轉。 如果需要,第一個運算元的係數在左側用零填充至精度長度。 第一個運算元的符號和指數不變。

same_quantum(other, context=None)

測試 self 和 other 是否具有相同的指數,或者兩者是否都是 NaN

此操作不受上下文影響,並且是安靜的:不會更改任何標誌,也不會執行任何舍入。作為例外,如果無法精確轉換第二個運算元,則 C 版本可能會引發 InvalidOperation。

scaleb(other, context=None)

返回第一個運算元,其指數由第二個運算元調整。等效地,返回第一個運算元乘以 10**other。第二個運算元必須是整數。

shift(other, context=None)

返回將第一個運算元的數字移動由第二個運算元指定的量後的結果。第二個運算元必須是 -precision 到 precision 範圍內的整數。第二個運算元的絕對值給出了移動的位數。如果第二個運算元為正數,則向左移動;否則向右移動。移入係數的數字為零。第一個運算元的符號和指數不變。

sqrt(context=None)

返回引數的平方根,精度為全精度。

to_eng_string(context=None)

轉換為字串,如果需要指數,則使用工程計數法。

工程計數法的指數是 3 的倍數。這可以在小數點左側保留最多 3 位數字,並且可能需要新增一個或兩個尾隨零。

例如,這將 Decimal('123E+1') 轉換為 Decimal('1.23E+3')

to_integral(rounding=None, context=None)

to_integral_value() 方法相同。to_integral 名稱保留是為了與舊版本相容。

to_integral_exact(rounding=None, context=None)

四捨五入到最接近的整數,如果發生舍入,則根據需要發出 InexactRounded 訊號。舍入模式由給定的 rounding 引數確定(如果給定),否則由給定的 context 確定。如果未給出任何引數,則使用當前上下文的舍入模式。

to_integral_value(rounding=None, context=None)

四捨五入到最接近的整數,而不發出 InexactRounded 訊號。如果給定,則應用*舍入*;否則,使用提供的*上下文*或當前上下文中的舍入方法。

可以使用 round() 函式對十進位制數進行舍入

round(number)
round(number, ndigits)

如果未給出*ndigits* 或 None,則返回最接近*number*的 int,四捨五入到偶數,並忽略 Decimal 上下文的舍入模式。如果*number*是無窮大,則引發 OverflowError,如果它是(安靜或發出訊號的)NaN,則引發 ValueError

如果*ndigits* 是一個 int,則會遵守上下文的舍入模式,並返回一個表示*number* 四捨五入到 Decimal('1E-ndigits') 最接近倍數的 Decimal;在這種情況下,round(number, ndigits) 等效於 self.quantize(Decimal('1E-ndigits'))。如果*number*是安靜的 NaN,則返回 Decimal('NaN')。如果*number*是無窮大、發出訊號的 NaN,或者如果量化操作後係數的長度大於當前上下文的精度,則引發 InvalidOperation。換句話說,對於非極端情況

  • 如果*ndigits*為正,則返回*number*四捨五入到*ndigits*位小數;

  • 如果*ndigits*為零,則返回*number*四捨五入到最接近的整數;

  • 如果*ndigits*為負數,則返回*number*四捨五入到 10**abs(ndigits) 最接近的倍數。

例如

>>> from decimal import Decimal, getcontext, ROUND_DOWN
>>> getcontext().rounding = ROUND_DOWN
>>> round(Decimal('3.75'))     # context rounding ignored
4
>>> round(Decimal('3.5'))      # round-ties-to-even
4
>>> round(Decimal('3.75'), 0)  # uses the context rounding
Decimal('3')
>>> round(Decimal('3.75'), 1)
Decimal('3.7')
>>> round(Decimal('3.75'), -1)
Decimal('0E+1')

邏輯運算元

logical_and()logical_invert()logical_or()logical_xor() 方法期望它們的引數為*邏輯運算元*。*邏輯運算元*是一個 Decimal 例項,其指數和符號均為零,並且其數字全部為 01

上下文物件

上下文是算術運算的環境。它們控制精度,設定舍入規則,確定哪些訊號被視為異常,並限制指數的範圍。

每個執行緒都有自己的當前上下文,可以使用 getcontext()setcontext() 函式訪問或更改。

decimal.getcontext()

返回活動執行緒的當前上下文。

decimal.setcontext(c)

將活動執行緒的當前上下文設定為*c*。

您還可以使用 with 語句和 localcontext() 函式來臨時更改活動上下文。

decimal.localcontext(ctx=None, **kwargs)

返回一個上下文管理器,它將在進入 with 語句時將活動執行緒的當前上下文設定為*ctx*的副本,並在退出 with 語句時恢復之前的上下文。如果沒有指定上下文,則使用當前上下文的副本。*kwargs* 引數用於設定新上下文的屬性。

例如,以下程式碼將當前十進位制精度設定為 42 位,執行計算,然後自動恢復之前的上下文

from decimal import localcontext

with localcontext() as ctx:
    ctx.prec = 42   # Perform a high precision calculation
    s = calculate_something()
s = +s  # Round the final result back to the default precision

使用關鍵字引數,程式碼將如下所示

from decimal import localcontext

with localcontext(prec=42) as ctx:
    s = calculate_something()
s = +s

如果 kwargs 提供的屬性是 TypeError 不支援的,則會引發 TypeError 異常。如果 kwargs 為某個屬性提供了無效值,則會引發 TypeErrorValueError 異常。

在 3.11 版本中更改: localcontext() 現在支援透過使用關鍵字引數來設定上下文屬性。

也可以使用下面描述的 Context 建構函式建立新的上下文。此外,該模組還提供了三個預定義的上下文。

class decimal.BasicContext

這是由《通用十進位制算術規範》定義的標準上下文。精度設定為 9。舍入設定為 ROUND_HALF_UP。所有標誌都被清除。除了 Inexact, RoundedSubnormal 之外的所有陷阱都被啟用(視為異常)。

由於啟用了許多陷阱,此上下文對於除錯非常有用。

class decimal.ExtendedContext

這是由《通用十進位制算術規範》定義的標準上下文。精度設定為 9。舍入設定為 ROUND_HALF_EVEN。所有標誌都被清除。不啟用任何陷阱(因此在計算過程中不會引發異常)。

由於停用了陷阱,因此此上下文適用於那些傾向於使用 NaNInfinity 作為結果值而不是引發異常的應用程式。這允許應用程式在存在可能導致程式停止的條件下完成執行。

class decimal.DefaultContext

此上下文被 Context 建構函式用作新上下文的原型。更改一個欄位(如精度)會影響 Context 建構函式建立的新上下文的預設值。

此上下文在多執行緒環境中最為有用。在啟動執行緒之前更改其中一個欄位可以設定系統範圍的預設值。不建議線上程啟動後更改欄位,因為它需要執行緒同步以防止競爭條件。

在單執行緒環境中,最好完全不使用此上下文。相反,只需如下所述顯式建立上下文即可。

預設值為 Context.prec=28, Context.rounding=ROUND_HALF_EVEN, 併為 OverflowInvalidOperationDivisionByZero 啟用陷阱。

除了提供的三個上下文之外,還可以使用 Context 建構函式建立新的上下文。

class decimal.Context(prec=None, rounding=None, Emin=None, Emax=None, capitals=None, clamp=None, flags=None, traps=None)

建立一個新的上下文。如果未指定欄位或欄位為 None,則預設值會從 DefaultContext 複製。如果未指定 *flags* 欄位或欄位為 None,則會清除所有標誌。

prec 是一個介於 [1, MAX_PREC] 之間的整數,用於設定上下文中算術運算的精度。

rounding 選項是 舍入模式 部分中列出的常量之一。

trapsflags 欄位列出要設定的任何訊號。通常,新上下文應僅設定陷阱並保持標誌清除狀態。

EminEmax 欄位是指定指數允許的外部限制的整數。Emin 必須在範圍 [MIN_EMIN, 0] 中,Emax 必須在範圍 [0, MAX_EMAX] 中。

capitals 欄位是 01 (預設)。如果設定為 1,則指數會使用大寫 E 列印;否則,會使用小寫 eDecimal('6.02e+23')

clamp 欄位是 0 (預設) 或 1。如果設定為 1,則在此上下文中可表示的 Decimal 例項的指數 e 嚴格限制在範圍 Emin - prec + 1 <= e <= Emax - prec + 1 內。如果 clamp0,則成立的條件較弱:Decimal 例項的調整後的指數最多為 Emax。當 clamp1 時,一個大的正常數(如果可能)會將其指數減小,並在其係數中新增相應數量的零,以符合指數約束;這保留了數字的值,但會丟失有關重要尾隨零的資訊。例如

>>> Context(prec=6, Emax=999, clamp=1).create_decimal('1.23e999')
Decimal('1.23000E+999')

clamp 值為 1 允許與 IEEE 754 中指定的固定寬度十進位制交換格式相容。

Context 類定義了一些通用的方法,以及大量用於在給定上下文中直接進行算術運算的方法。此外,對於上面描述的每個 Decimal 方法(除了 adjusted()as_tuple() 方法),都有一個對應的 Context 方法。例如,對於一個 Context 例項 C 和一個 Decimal 例項 xC.exp(x) 等同於 x.exp(context=C)。每個 Context 方法都接受 Python 整數(int 的例項),如同接受 Decimal 例項一樣。

clear_flags()

將所有標誌重置為 0

clear_traps()

將所有陷阱重置為 0

3.3 版本新增。

copy()

返回上下文的副本。

copy_decimal(num)

返回 Decimal 例項 num 的副本。

create_decimal(num)

num 建立一個新的 Decimal 例項,但使用 self 作為上下文。與 Decimal 建構函式不同,上下文精度、舍入方法、標誌和陷阱會應用於轉換。

這很有用,因為常量通常以比應用程式需要的更高的精度給出。另一個好處是,舍入會立即消除超出當前精度的數字帶來的意外影響。在以下示例中,使用未舍入的輸入意味著將零新增到總和可以更改結果

>>> getcontext().prec = 3
>>> Decimal('3.4445') + Decimal('1.0023')
Decimal('4.45')
>>> Decimal('3.4445') + Decimal(0) + Decimal('1.0023')
Decimal('4.44')

此方法實現了 IBM 規範的 to-number 操作。如果引數是字串,則不允許有前導或尾隨的空格或下劃線。

create_decimal_from_float(f)

從浮點數 f 建立一個新的 Decimal 例項,但使用 self 作為上下文進行舍入。與 Decimal.from_float() 類方法不同,上下文精度、舍入方法、標誌和陷阱會應用於轉換。

>>> context = Context(prec=5, rounding=ROUND_DOWN)
>>> context.create_decimal_from_float(math.pi)
Decimal('3.1415')
>>> context = Context(prec=5, traps=[Inexact])
>>> context.create_decimal_from_float(math.pi)
Traceback (most recent call last):
    ...
decimal.Inexact: None

3.1 版本新增。

Etiny()

返回一個等於 Emin - prec + 1 的值,這是次正規結果的最小指數值。當發生下溢時,指數將設定為 Etiny

Etop()

返回一個等於 Emax - prec + 1 的值。

處理小數的通常方法是建立 Decimal 例項,然後應用算術運算,這些運算在活動執行緒的當前上下文中進行。另一種方法是使用上下文方法在特定上下文中進行計算。這些方法與 Decimal 類的方法類似,這裡只做簡要介紹。

abs(x)

返回 x 的絕對值。

add(x, y)

返回 xy 的和。

canonical(x)

返回相同的 Decimal 物件 x

compare(x, y)

在數值上比較 xy

compare_signal(x, y)

在數值上比較兩個運算元的值。

compare_total(x, y)

使用抽象表示形式比較兩個運算元。

compare_total_mag(x, y)

使用抽象表示形式比較兩個運算元,忽略符號。

copy_abs(x)

返回 x 的副本,並將符號設定為 0。

copy_negate(x)

返回 x 的副本,並將符號取反。

copy_sign(x, y)

y 的符號複製到 x

divide(x, y)

返回 x 除以 y 的結果。

divide_int(x, y)

返回 x 除以 y 的結果,截斷為整數。

divmod(x, y)

將兩個數字相除,並返回結果的整數部分。

exp(x)

返回 e ** x

fma(x, y, z)

返回 x 乘以 y,加上 z 的結果。

is_canonical(x)

如果 x 是規範的,則返回 True;否則返回 False

is_finite(x)

如果 x 是有限的,則返回 True;否則返回 False

is_infinite(x)

如果 x 是無限的,則返回 True;否則返回 False

is_nan(x)

如果 x 是 qNaN 或 sNaN,則返回 True;否則返回 False

is_normal(x)

如果 x 是一個正規數,則返回 True;否則返回 False

is_qnan(x)

如果 x 是一個安靜的 NaN,則返回 True;否則返回 False

is_signed(x)

如果 x 是負數,則返回 True;否則返回 False

is_snan(x)

如果 x 是一個訊號 NaN,則返回 True;否則返回 False

is_subnormal(x)

如果 x 是次正規數,則返回 True;否則返回 False

is_zero(x)

如果 x 是零,則返回 True;否則返回 False

ln(x)

返回 x 的自然對數(底為 e)。

log10(x)

返回 x 的以 10 為底的對數。

logb(x)

返回運算元 MSD 的大小的指數。

logical_and(x, y)

在每個運算元的數字之間應用邏輯運算

logical_invert(x)

反轉 x 中的所有數字。

logical_or(x, y)

在每個運算元的數字之間應用邏輯運算

logical_xor(x, y)

在每個運算元的數字之間應用邏輯運算異或

max(x, y)

以數值方式比較兩個值,並返回最大值。

max_mag(x, y)

以數值方式比較這些值,忽略它們的符號。

min(x, y)

以數值方式比較兩個值,並返回最小值。

min_mag(x, y)

以數值方式比較這些值,忽略它們的符號。

minus(x)

Minus 對應於 Python 中的一元字首減號運算子。

multiply(x, y)

返回 xy 的乘積。

next_minus(x)

返回小於 x 的最大可表示數字。

next_plus(x)

返回大於 x 的最小可表示數字。

next_toward(x, y)

返回最接近 x,並朝向 y 方向的數字。

normalize(x)

x 簡化為其最簡形式。

number_class(x)

返回 x 的類別的指示。

plus(x)

Plus 對應於 Python 中的一元字首加號運算子。此操作應用上下文精度和舍入,因此它不是一個恆等運算。

power(x, y, modulo=None)

返回 xy 次冪,如果給定了 modulo,則對 modulo 取模。

使用兩個引數,計算 x**y。如果 x 為負數,則 y 必須為整數。除非 y 為整數,並且結果為有限的並且可以用 ‘precision’ 位數精確表示,否則結果將不精確。使用上下文的舍入模式。Python 版本中的結果始終正確舍入。

Decimal(0) ** Decimal(0) 會導致 InvalidOperation,如果 InvalidOperation 沒有被捕獲,則會導致 Decimal('NaN')

在 3.3 版本中更改: C 模組根據正確舍入的 exp()ln() 函式計算 power()。結果是明確定義的,但僅“幾乎總是正確舍入”。

使用三個引數,計算 (x**y) % modulo。對於三引數形式,以下引數限制適用

  • 所有三個引數必須為整數

  • y 必須為非負數

  • xy 中至少有一個必須為非零

  • modulo 必須為非零,並且最多具有 ‘precision’ 位數

Context.power(x, y, modulo) 產生的值等於透過計算具有無界精度的 (x**y) % modulo 獲得的值,但是計算效率更高。結果的指數為零,而與 xymodulo 的指數無關。結果始終是精確的。

量化(x, y)

返回一個值,該值等於x(已舍入),且具有y的指數。

基數()

只返回 10,因為這是 Decimal,:)

餘數(x, y)

返回整數除法的餘數。

如果結果非零,則結果的符號與原始被除數的符號相同。

臨近餘數(x, y)

返回 x - y * n,其中 *n* 是最接近 x / y 精確值的整數(如果結果為 0,則其符號將與 *x* 的符號相同)。

迴圈(x, y)

返回 *x* 的迴圈副本,迴圈 *y* 次。

相同量子(x, y)

如果兩個運算元具有相同的指數,則返回 True

scaleb(x, y)

返回第一個運算元,並在其 exp 中加上第二個值後。

移位(x, y)

返回 *x* 的移位副本,移位 *y* 次。

平方根(x)

在上下文精度範圍內,一個非負數的平方根。

相減(x, y)

返回 *x* 和 *y* 之間的差值。

轉為工程字串(x)

轉換為字串,如果需要指數,則使用工程計數法。

工程計數法的指數是 3 的倍數。這可以在小數點左側保留最多 3 位數字,並且可能需要新增一個或兩個尾隨零。

轉為精確整數(x)

舍入為整數。

轉為科學計數法字串(x)

使用科學計數法將數字轉換為字串。

常量

本節中的常量僅與 C 模組相關。 為了相容性,它們也包含在純 Python 版本中。

32 位

64 位

decimal.MAX_PREC

425000000

999999999999999999

decimal.MAX_EMAX

425000000

999999999999999999

decimal.MIN_EMIN

-425000000

-999999999999999999

decimal.MIN_ETINY

-849999999

-1999999999999999997

decimal.HAVE_THREADS

該值為 True。 已棄用,因為 Python 現在始終具有執行緒。

自 3.9 版本起已棄用。

decimal.HAVE_CONTEXTVAR

預設值為 True。 如果 Python 使用 使用 --without-decimal-contextvar 選項配置,則 C 版本使用執行緒區域性而非協程區域性上下文,並且該值為 False。 在某些巢狀上下文場景中,這會稍微快一些。

在 3.8.3 版本中新增。

舍入模式

decimal.ROUND_CEILING

Infinity 舍入。

decimal.ROUND_DOWN

向零舍入。

decimal.ROUND_FLOOR

-Infinity 舍入。

decimal.ROUND_HALF_DOWN

舍入到最接近的數,其中關係走向零。

decimal.ROUND_HALF_EVEN

舍入到最接近的數,其中關係走向最接近的偶數整數。

decimal.ROUND_HALF_UP

舍入到最接近的數,其中關係遠離零。

decimal.ROUND_UP

遠離零舍入。

decimal.ROUND_05UP

如果向零舍入後的最後一位數字為 0 或 5,則遠離零舍入; 否則向零舍入。

訊號

訊號表示計算過程中出現的條件。 每個訊號對應一個上下文標誌和一個上下文陷阱啟用器。

每當遇到該條件時,都會設定上下文標誌。 計算之後,可以檢查標誌以獲取資訊(例如,確定計算是否精確)。 檢查完標誌後,請務必在開始下一次計算之前清除所有標誌。

如果為訊號設定了上下文的陷阱啟用器,則該條件會導致引發 Python 異常。 例如,如果設定了 DivisionByZero 陷阱,則在遇到該條件時會引發 DivisionByZero 異常。

class decimal.Clamped

更改了指數以適應表示約束。

通常,當指數超出上下文的 EminEmax 限制時,會發生鉗位。 如果可能,透過在係數中新增零來減少指數以適應。

class decimal.DecimalException

其他訊號的基類和 ArithmeticError 的子類。

class decimal.DivisionByZero

表示非無窮數除以零的訊號。

可能發生在除法、模除法或將數字提高到負冪時。 如果未捕獲此訊號,則返回 Infinity-Infinity,其符號由計算的輸入確定。

class decimal.Inexact

指示發生了舍入且結果不精確。

當在舍入期間丟棄非零數字時發出訊號。 返回舍入後的結果。 訊號標誌或陷阱用於檢測結果何時不精確。

class decimal.InvalidOperation

執行了無效操作。

指示請求的操作沒有意義。 如果沒有捕獲,則返回 NaN。 可能的原因包括

Infinity - Infinity
0 * Infinity
Infinity / Infinity
x % 0
Infinity % x
sqrt(-x) and x > 0
0 ** 0
x ** (non-integer)
x ** Infinity
class decimal.Overflow

數值溢位。

表示在舍入發生後,指數大於 Context.Emax。如果未被捕獲,則結果取決於舍入模式,要麼向內拉到最大的可表示有限數,要麼向外舍入到 Infinity。無論哪種情況,都會同時發出 InexactRounded 訊號。

class decimal.Rounded

發生了舍入,儘管可能沒有資訊丟失。

每當舍入丟棄數字時發出訊號;即使這些數字為零(例如將 5.00 舍入到 5.0)。如果未被捕獲,則返回結果不變。此訊號用於檢測有效數字的丟失。

class decimal.Subnormal

舍入之前,指數低於 Emin

當操作結果為次正規數(指數太小)時發生。如果未被捕獲,則返回結果不變。

class decimal.Underflow

數值下溢,結果舍入為零。

當次正規結果被舍入為零時發生。同時發出 InexactSubnormal 訊號。

class decimal.FloatOperation

啟用更嚴格的浮點數和 Decimal 混合語義。

如果訊號未被捕獲(預設),則允許在 Decimal 建構函式、 create_decimal() 和所有比較運算子中混合使用浮點數和 Decimal。轉換和比較都是精確的。任何混合操作的發生都會透過在上下文標誌中設定 FloatOperation 來靜默記錄。使用 from_float()create_decimal_from_float() 的顯式轉換不會設定該標誌。

否則(訊號被捕獲),只有相等比較和顯式轉換是靜默的。所有其他混合操作都會引發 FloatOperation

下表總結了訊號的層次結構

exceptions.ArithmeticError(exceptions.Exception)
    DecimalException
        Clamped
        DivisionByZero(DecimalException, exceptions.ZeroDivisionError)
        Inexact
            Overflow(Inexact, Rounded)
            Underflow(Inexact, Rounded, Subnormal)
        InvalidOperation
        Rounded
        Subnormal
        FloatOperation(DecimalException, exceptions.TypeError)

浮點數說明

透過提高精度來減輕舍入誤差

使用十進位制浮點數消除了十進位制表示誤差(使得可以精確表示 0.1);但是,當非零數字超過固定精度時,某些操作仍然會產生舍入誤差。

舍入誤差的影響可以透過加法或減法幾乎相互抵消的量來放大,從而導致有效數字的損失。Knuth 提供了兩個具有指導意義的示例,其中精度不足的舍入浮點運算會導致加法的結合律和分配律失效

# Examples from Seminumerical Algorithms, Section 4.2.2.
>>> from decimal import Decimal, getcontext
>>> getcontext().prec = 8

>>> u, v, w = Decimal(11111113), Decimal(-11111111), Decimal('7.51111111')
>>> (u + v) + w
Decimal('9.5111111')
>>> u + (v + w)
Decimal('10')

>>> u, v, w = Decimal(20000), Decimal(-6), Decimal('6.0000003')
>>> (u*v) + (u*w)
Decimal('0.01')
>>> u * (v+w)
Decimal('0.0060000')

decimal 模組可以透過擴充套件精度以避免有效數字的損失來恢復這些恆等式

>>> getcontext().prec = 20
>>> u, v, w = Decimal(11111113), Decimal(-11111111), Decimal('7.51111111')
>>> (u + v) + w
Decimal('9.51111111')
>>> u + (v + w)
Decimal('9.51111111')
>>>
>>> u, v, w = Decimal(20000), Decimal(-6), Decimal('6.0000003')
>>> (u*v) + (u*w)
Decimal('0.0060000')
>>> u * (v+w)
Decimal('0.0060000')

特殊值

decimal 模組的數字系統提供特殊值,包括 NaNsNaN-InfinityInfinity 和兩個零,+0-0

可以使用:Decimal('Infinity') 直接構造無窮大。此外,當 DivisionByZero 訊號未被捕獲時,它們也可能因除以零而產生。同樣,當 Overflow 訊號未被捕獲時,無窮大可能因舍入超出最大可表示數的限制而產生。

無窮大是有符號的(仿射),並且可以在算術運算中使用,其中它們被視為非常大的、不確定的數字。例如,將一個常數加到無窮大上會得到另一個無限的結果。

一些運算是不確定的並返回 NaN,或者如果 InvalidOperation 訊號被捕獲,則引發異常。例如,0/0 返回 NaN,表示“不是數字”。這種 NaN 是靜默的,一旦建立,它將在其他計算中流動,總是產生另一個 NaN。此行為對於偶爾缺少輸入的一系列計算很有用 — 它允許計算繼續進行,同時將特定結果標記為無效。

一個變體是 sNaN,它在每次操作後都會發出訊號,而不是保持靜默。當無效結果需要中斷計算以進行特殊處理時,這是一個有用的返回值。

Python 的比較運算子的行為在涉及 NaN 時可能會有些令人驚訝。當其中一個運算元是安靜或發出訊號的 NaN 時,相等性測試總是返回 False(即使在執行 Decimal('NaN')==Decimal('NaN') 時),而不等性測試總是返回 True。如果任一運算元是 NaN,則嘗試使用任何 <<=>>= 運算子比較兩個 Decimal 將引發 InvalidOperation 訊號,如果此訊號未被捕獲,則返回 False。請注意,通用十進位制算術規範未指定直接比較的行為;這些涉及 NaN 的比較規則取自 IEEE 854 標準(參見 5.7 節中的表 3)。為了確保嚴格符合標準,請改用 compare()compare_signal() 方法。

帶符號的零可能來自下溢的計算。它們保留如果計算以更高的精度執行本應產生的符號。由於它們的大小為零,因此正零和負零都被視為相等,並且它們的符號是資訊性的。

除了兩個帶符號的零,它們是不同的但相等的之外,還有各種精度不同但值等效的零的表示形式。這需要一些時間來適應。對於習慣於規範化浮點數表示的人來說,以下計算返回的值等於零這一點並不明顯

>>> 1 / Decimal('Infinity')
Decimal('0E-1000026')

使用執行緒

getcontext() 函式訪問每個執行緒的不同 Context 物件。擁有單獨的執行緒上下文意味著執行緒可以進行更改(例如 getcontext().prec=10),而不會干擾其他執行緒。

同樣,setcontext() 函式會自動將其目標分配給當前執行緒。

如果在呼叫 setcontext() 之前沒有呼叫過 getcontext(),則 getcontext() 將自動建立一個新的上下文,供當前執行緒使用。

新上下文是從名為 *DefaultContext* 的原型上下文複製的。 要控制預設值,以便每個執行緒在整個應用程式中使用相同的值,請直接修改 *DefaultContext* 物件。 這應該在任何執行緒啟動 *之前* 完成,這樣線上程呼叫 getcontext() 之間就不會出現競爭條件。 例如:

# Set applicationwide defaults for all threads about to be launched
DefaultContext.prec = 12
DefaultContext.rounding = ROUND_DOWN
DefaultContext.traps = ExtendedContext.traps.copy()
DefaultContext.traps[InvalidOperation] = 1
setcontext(DefaultContext)

# Afterwards, the threads can be started
t1.start()
t2.start()
t3.start()
 . . .

示例

這裡有一些示例,它們充當實用函式,並演示了使用 Decimal 類的方法

def moneyfmt(value, places=2, curr='', sep=',', dp='.',
             pos='', neg='-', trailneg=''):
    """Convert Decimal to a money formatted string.

    places:  required number of places after the decimal point
    curr:    optional currency symbol before the sign (may be blank)
    sep:     optional grouping separator (comma, period, space, or blank)
    dp:      decimal point indicator (comma or period)
             only specify as blank when places is zero
    pos:     optional sign for positive numbers: '+', space or blank
    neg:     optional sign for negative numbers: '-', '(', space or blank
    trailneg:optional trailing minus indicator:  '-', ')', space or blank

    >>> d = Decimal('-1234567.8901')
    >>> moneyfmt(d, curr='$')
    '-$1,234,567.89'
    >>> moneyfmt(d, places=0, sep='.', dp='', neg='', trailneg='-')
    '1.234.568-'
    >>> moneyfmt(d, curr='$', neg='(', trailneg=')')
    '($1,234,567.89)'
    >>> moneyfmt(Decimal(123456789), sep=' ')
    '123 456 789.00'
    >>> moneyfmt(Decimal('-0.02'), neg='<', trailneg='>')
    '<0.02>'

    """
    q = Decimal(10) ** -places      # 2 places --> '0.01'
    sign, digits, exp = value.quantize(q).as_tuple()
    result = []
    digits = list(map(str, digits))
    build, next = result.append, digits.pop
    if sign:
        build(trailneg)
    for i in range(places):
        build(next() if digits else '0')
    if places:
        build(dp)
    if not digits:
        build('0')
    i = 0
    while digits:
        build(next())
        i += 1
        if i == 3 and digits:
            i = 0
            build(sep)
    build(curr)
    build(neg if sign else pos)
    return ''.join(reversed(result))

def pi():
    """Compute Pi to the current precision.

    >>> print(pi())
    3.141592653589793238462643383

    """
    getcontext().prec += 2  # extra digits for intermediate steps
    three = Decimal(3)      # substitute "three=3.0" for regular floats
    lasts, t, s, n, na, d, da = 0, three, 3, 1, 0, 0, 24
    while s != lasts:
        lasts = s
        n, na = n+na, na+8
        d, da = d+da, da+32
        t = (t * n) / d
        s += t
    getcontext().prec -= 2
    return +s               # unary plus applies the new precision

def exp(x):
    """Return e raised to the power of x.  Result type matches input type.

    >>> print(exp(Decimal(1)))
    2.718281828459045235360287471
    >>> print(exp(Decimal(2)))
    7.389056098930650227230427461
    >>> print(exp(2.0))
    7.38905609893
    >>> print(exp(2+0j))
    (7.38905609893+0j)

    """
    getcontext().prec += 2
    i, lasts, s, fact, num = 0, 0, 1, 1, 1
    while s != lasts:
        lasts = s
        i += 1
        fact *= i
        num *= x
        s += num / fact
    getcontext().prec -= 2
    return +s

def cos(x):
    """Return the cosine of x as measured in radians.

    The Taylor series approximation works best for a small value of x.
    For larger values, first compute x = x % (2 * pi).

    >>> print(cos(Decimal('0.5')))
    0.8775825618903727161162815826
    >>> print(cos(0.5))
    0.87758256189
    >>> print(cos(0.5+0j))
    (0.87758256189+0j)

    """
    getcontext().prec += 2
    i, lasts, s, fact, num, sign = 0, 0, 1, 1, 1, 1
    while s != lasts:
        lasts = s
        i += 2
        fact *= i * (i-1)
        num *= x * x
        sign *= -1
        s += num / fact * sign
    getcontext().prec -= 2
    return +s

def sin(x):
    """Return the sine of x as measured in radians.

    The Taylor series approximation works best for a small value of x.
    For larger values, first compute x = x % (2 * pi).

    >>> print(sin(Decimal('0.5')))
    0.4794255386042030002732879352
    >>> print(sin(0.5))
    0.479425538604
    >>> print(sin(0.5+0j))
    (0.479425538604+0j)

    """
    getcontext().prec += 2
    i, lasts, s, fact, num, sign = 1, 0, x, 1, x, 1
    while s != lasts:
        lasts = s
        i += 2
        fact *= i * (i-1)
        num *= x * x
        sign *= -1
        s += num / fact * sign
    getcontext().prec -= 2
    return +s

Decimal 常見問題

問:鍵入 decimal.Decimal('1234.5') 很麻煩。 在使用互動式直譯器時,有沒有辦法減少鍵入次數?

答:一些使用者將建構函式縮寫為單個字母

>>> D = decimal.Decimal
>>> D('1.23') + D('3.45')
Decimal('4.68')

問:在具有兩位小數的定點應用程式中,某些輸入具有很多位,需要進行舍入。 其他輸入不應有多餘的數字,需要進行驗證。 應該使用什麼方法?

答:quantize() 方法會舍入到固定的小數位數。 如果設定了 Inexact 陷阱,它也很有用於驗證

>>> TWOPLACES = Decimal(10) ** -2       # same as Decimal('0.01')
>>> # Round to two places
>>> Decimal('3.214').quantize(TWOPLACES)
Decimal('3.21')
>>> # Validate that a number does not exceed two places
>>> Decimal('3.21').quantize(TWOPLACES, context=Context(traps=[Inexact]))
Decimal('3.21')
>>> Decimal('3.214').quantize(TWOPLACES, context=Context(traps=[Inexact]))
Traceback (most recent call last):
   ...
Inexact: None

問:一旦我有了有效的兩位小數輸入,如何才能在整個應用程式中保持這種不變性?

答:某些操作(如加法、減法和整數乘法)會自動保留定點。 其他操作(如除法和非整數乘法)會更改小數位數,並且需要進行 quantize() 步驟來跟進

>>> a = Decimal('102.72')           # Initial fixed-point values
>>> b = Decimal('3.17')
>>> a + b                           # Addition preserves fixed-point
Decimal('105.89')
>>> a - b
Decimal('99.55')
>>> a * 42                          # So does integer multiplication
Decimal('4314.24')
>>> (a * b).quantize(TWOPLACES)     # Must quantize non-integer multiplication
Decimal('325.62')
>>> (b / a).quantize(TWOPLACES)     # And quantize division
Decimal('0.03')

在開發定點應用程式時,定義函式來處理 quantize() 步驟很方便

>>> def mul(x, y, fp=TWOPLACES):
...     return (x * y).quantize(fp)
...
>>> def div(x, y, fp=TWOPLACES):
...     return (x / y).quantize(fp)
>>> mul(a, b)                       # Automatically preserve fixed-point
Decimal('325.62')
>>> div(b, a)
Decimal('0.03')

問:有很多方法可以表示相同的值。 數字 200200.0002E2.02E+4 在不同的精度下都具有相同的值。 有沒有辦法將它們轉換為單個可識別的規範值?

答:normalize() 方法將所有等效值對映到單個代表值

>>> values = map(Decimal, '200 200.000 2E2 .02E+4'.split())
>>> [v.normalize() for v in values]
[Decimal('2E+2'), Decimal('2E+2'), Decimal('2E+2'), Decimal('2E+2')]

問:計算中何時會發生舍入?

答:它發生在計算 *之後*。 decimal 規範的理念是,數字被認為是精確的,並且在建立時與當前上下文無關。 它們甚至可以具有比當前上下文更高的精度。 計算使用這些精確的輸入進行處理,然後將舍入(或其他上下文操作)應用於計算的 *結果*

>>> getcontext().prec = 5
>>> pi = Decimal('3.1415926535')   # More than 5 digits
>>> pi                             # All digits are retained
Decimal('3.1415926535')
>>> pi + 0                         # Rounded after an addition
Decimal('3.1416')
>>> pi - Decimal('0.00005')        # Subtract unrounded numbers, then round
Decimal('3.1415')
>>> pi + 0 - Decimal('0.00005').   # Intermediate values are rounded
Decimal('3.1416')

問:某些十進位制值始終以指數表示法列印。 有沒有辦法獲得非指數表示?

答:對於某些值,指數表示法是表達係數中有效位數數量的唯一方法。 例如,將 5.0E+3 表示為 5000 可以保持值不變,但不能顯示原始值的兩位有效位。

如果應用程式不關心跟蹤有效位數,則很容易刪除指數和尾隨零,從而失去有效位數,但保持值不變

>>> def remove_exponent(d):
...     return d.quantize(Decimal(1)) if d == d.to_integral() else d.normalize()
>>> remove_exponent(Decimal('5E+3'))
Decimal('5000')

問:有沒有辦法將常規浮點數轉換為 Decimal

答:是的,任何二進位制浮點數都可以精確地表示為 Decimal,儘管精確轉換可能需要比直覺所建議的更高的精度

>>> Decimal(math.pi)
Decimal('3.141592653589793115997963468544185161590576171875')

問:在複雜的計算中,我如何確保我沒有因為精度不足或舍入異常而得到虛假的結果?

答:decimal 模組可以輕鬆測試結果。 最佳實踐是使用更高的精度和各種舍入模式重新執行計算。 差異很大的結果表示精度不足、舍入模式問題、輸入條件不良或演算法數值不穩定。

問:我注意到上下文精度應用於操作的結果,但不應用於輸入。 混合使用不同精度的值時,有什麼需要注意的嗎?

答:是的。 原則是所有值都被認為是精確的,因此對這些值的算術運算也是精確的。 只有結果會被舍入。 輸入的優點是“所見即所得”。 缺點是,如果您忘記輸入沒有被舍入,結果可能會看起來很奇怪

>>> getcontext().prec = 3
>>> Decimal('3.104') + Decimal('2.104')
Decimal('5.21')
>>> Decimal('3.104') + Decimal('0.000') + Decimal('2.104')
Decimal('5.20')

解決方案是提高精度或使用一元加號操作強制舍入輸入

>>> getcontext().prec = 3
>>> +Decimal('1.23456789')      # unary plus triggers rounding
Decimal('1.23')

或者,可以使用 Context.create_decimal() 方法在建立時舍入輸入

>>> Context(prec=5, rounding=ROUND_DOWN).create_decimal('1.2345678')
Decimal('1.2345')

問:CPython 實現對於大數是否快速?

答:是的。 在 CPython 和 PyPy3 實現中,decimal 模組的 C/CFFI 版本集成了高速 libmpdec 庫,用於任意精度、正確舍入的十進位制浮點算術[1]libmpdec 對於中等大小的數字使用 Karatsuba 乘法,對於非常大的數字使用 數論變換

必須調整上下文以進行精確的任意精度算術。 EminEmax 應始終設定為最大值,clamp 應始終為 0(預設值)。 設定 prec 需要小心。

嘗試大數算術的最簡單方法是將 prec 的最大值也用於 [2]

>>> setcontext(Context(prec=MAX_PREC, Emax=MAX_EMAX, Emin=MIN_EMIN))
>>> x = Decimal(2) ** 256
>>> x / 128
Decimal('904625697166532776746648320380374280103671755200316906558262375061821325312')

對於不精確的結果,MAX_PREC 在 64 位平臺上過大,可用記憶體將不足

>>> Decimal(1) / 3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
MemoryError

在具有過度分配的系統(例如 Linux)上,一種更復雜的方法是將 prec 調整為可用 RAM 的大小。 假設您有 8GB 的 RAM,並且期望 10 個同時執行的運算元,每個運算元最多使用 500MB

>>> import sys
>>>
>>> # Maximum number of digits for a single operand using 500MB in 8-byte words
>>> # with 19 digits per word (4-byte and 9 digits for the 32-bit build):
>>> maxdigits = 19 * ((500 * 1024**2) // 8)
>>>
>>> # Check that this works:
>>> c = Context(prec=maxdigits, Emax=MAX_EMAX, Emin=MIN_EMIN)
>>> c.traps[Inexact] = True
>>> setcontext(c)
>>>
>>> # Fill the available precision with nines:
>>> x = Decimal(0).logical_invert() * 9
>>> sys.getsizeof(x)
524288112
>>> x + 2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  decimal.Inexact: [<class 'decimal.Inexact'>]

一般來說(尤其是在沒有過度分配的系統上),建議估計更嚴格的範圍,並且如果期望所有計算都精確,則設定 Inexact 陷阱。