decimal
--- 十進(jìn)制定點(diǎn)和浮點(diǎn)運算?
源碼: Lib/decimal.py
decimal
模塊為快速正確舍入的十進(jìn)制浮點(diǎn)運算提供支持。 與 float
數據類(lèi)型相比,它具有以下幾個(gè)優(yōu)點(diǎn):
Decimal 類(lèi)型的“設計是基于考慮人類(lèi)習慣的浮點(diǎn)數模型,并且因此具有以下最高指導原則 —— 計算機必須提供與人們在學(xué)校所學(xué)習的算術(shù)相一致的算術(shù)?!?—— 摘自 decimal 算術(shù)規范描述。
Decimal 數字的表示是完全精確的。 相比之下,
1.1
和2.2
這樣的數字在二進(jìn)制浮點(diǎn)中沒(méi)有精確的表示。 最終用戶(hù)通常不希望1.1 + 2.2
如二進(jìn)制浮點(diǎn)數表示那樣被顯示為3.3000000000000003
。精確性會(huì )延續到算術(shù)類(lèi)操作中。 對于 decimal 浮點(diǎn)數,
0.1 + 0.1 + 0.1 - 0.3
會(huì )精確地等于零。 而對于二進(jìn)制浮點(diǎn)數,結果則為5.5511151231257827e-017
。 雖然接近于零,但其中的誤差將妨礙可靠的相等性檢驗,并且誤差還會(huì )不斷累積。 因此,decimal 更適合具有嚴格相等不變性要求的會(huì )計類(lèi)應用。十進(jìn)制模塊包含有效位的概念,因此
1.30 + 1.20
的結果是2.50
。 保留尾隨零以表示有效位。 這是貨幣的慣用表示方法。乘法則沿用 “教科書(shū)“ 中:保留被乘數中的所有數字的方法。 例如,1.3 * 1.2
結果是1.56
而1.30 * 1.20
結果是1.5600
。與基于硬件的二進(jìn)制浮點(diǎn)不同,十進(jìn)制模塊具有用戶(hù)可更改的精度(默認為28位),可以與給定問(wèn)題所需的一樣大:
>>> from decimal import * >>> getcontext().prec = 6 >>> Decimal(1) / Decimal(7) Decimal('0.142857') >>> getcontext().prec = 28 >>> Decimal(1) / Decimal(7) Decimal('0.1428571428571428571428571429')
二進(jìn)制和 decimal 浮點(diǎn)數都是根據已發(fā)布的標準實(shí)現的。 雖然內置浮點(diǎn)類(lèi)型只公開(kāi)其功能的一小部分,但 decimal 模塊公開(kāi)了標準的所有必需部分。 在需要時(shí),程序員可以完全控制舍入和信號處理。 這包括通過(guò)使用異常來(lái)阻止任何不精確操作來(lái)強制執行精確算術(shù)的選項。
decimal 模塊旨在支持“無(wú)偏差,精確無(wú)舍入的十進(jìn)制算術(shù)(有時(shí)稱(chēng)為定點(diǎn)數算術(shù))和有舍入的浮點(diǎn)數算術(shù)”。 —— 摘自 decimal 算術(shù)規范說(shuō)明。
該模塊的設計以三個(gè)概念為中心:decimal 數值,算術(shù)上下文和信號。
decimal 數值是不可變對象。 它由符號,系數和指數位組成。 為了保持有效位,系數位不會(huì )截去末尾零。 decimal 數值也包括特殊值例如 Infinity
,-Infinity
和 NaN
。 該標準還區分 -0
和 +0
。
算術(shù)的上下文是指定精度、舍入規則、指數限制、指示操作結果的標志以及確定符號是否被視為異常的陷阱啟用器的環(huán)境。 舍入選項包括 ROUND_CEILING
、 ROUND_DOWN
、 ROUND_FLOOR
、 ROUND_HALF_DOWN
, ROUND_HALF_EVEN
、 ROUND_HALF_UP
、 ROUND_UP
以及 ROUND_05UP
.
信號是在計算過(guò)程中出現的異常條件組。 根據應用程序的需要,信號可能會(huì )被忽略,被視為信息,或被視為異常。 十進(jìn)制模塊中的信號有:Clamped
、 InvalidOperation
、 DivisionByZero
、 Inexact
、 Rounded
、 Subnormal
、 Overflow
、 Underflow
以及 FloatOperation
。
對于每個(gè)信號,都有一個(gè)標志和一個(gè)陷阱啟動(dòng)器。 遇到信號時(shí),其標志設置為 1 ,然后,如果陷阱啟用器設置為 1 ,則引發(fā)異常。 標志是粘性的,因此用戶(hù)需要在監控計算之前重置它們。
參見(jiàn)
IBM 的通用十進(jìn)制算術(shù)規范描述 The General Decimal Arithmetic Specification。
快速入門(mén)教程?
通常使用 decimal 的方式是先導入該模塊,通過(guò) getcontext()
查看當前上下文,并在必要時(shí)為精度、舍入或啟用的陷阱設置新值:
>>> 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
可以基于整數、字符串、浮點(diǎn)數或元組構造 Decimal 實(shí)例。 基于整數或浮點(diǎn)數構造將執行該整數或浮點(diǎn)值的精確轉換。 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
信號被捕獲,構造函數中的小數和浮點(diǎn)數的意外混合或排序比較會(huì )引發(fā)異常
>>> 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 的重要性?xún)H由輸入的位數決定。 上下文精度和舍入僅在算術(shù)運算期間發(fā)揮作用。
>>> 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 版本的內部限制,則構造一個(gè) decimal 將引發(fā) InvalidOperation
>>> Decimal("1e9999999999999999999")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
decimal.InvalidOperation: [<class 'decimal.InvalidOperation'>]
在 3.3 版更改.
Decimal 數字能很好地與 Python 的其余部分交互。 以下是一個(gè)小小的 decimal 浮點(diǎn)數飛行馬戲團:
>>> 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 也可以使用一些數學(xué)函數:
>>> 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()
函數訪(fǎng)問(wèn)當前上下文并允許更改設置。 這種方法滿(mǎn)足大多數應用程序的需求。
對于更高級的工作,使用 Context() 構造函數創(chuàng )建備用上下文可能很有用。 要使用備用活動(dòng),請使用 setcontext()
函數。
根據標準,decimal
模塊提供了兩個(gè)現成的標準上下文 BasicContext
和 ExtendedContext
。 前者對調試特別有用,因為許多陷阱都已啟用:
>>> 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
上下文還具有用于監視計算期間遇到的異常情況的信號標志。 標志保持設置直到明確清除,因此最好通過(guò)使用 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
字段中的字典設置單個(gè)陷阱:
>>> 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
大多數程序僅在程序開(kāi)始時(shí)調整當前上下文一次。 并且,在許多應用程序中,數據在循環(huán)內單個(gè)強制轉換為 Decimal
。 通過(guò)創(chuàng )建上下文集和小數,程序的大部分操作數據與其他 Python 數字類(lèi)型沒(méi)有區別。
Decimal 對象?
- class decimal.Decimal(value='0', context=None)?
根據 value 構造一個(gè)新的
Decimal
對象。value 可以是整數,字符串,元組,
float
,或另一個(gè)Decimal
對象。 如果沒(méi)有給出 value,則返回Decimal('0')
。 如果 value 是一個(gè)字符串,它應該在前導和尾隨空格字符以及下劃線(xiàn)被刪除之后符合十進(jìn)制數字字符串語(yǔ)法: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
時(shí)也允許其他十進(jìn)制數碼。 其中包括來(lái)自各種其他語(yǔ)言系統的十進(jìn)制數碼(例如阿拉伯-印地語(yǔ)和天城文的數碼)以及全寬數碼'\uff10'
到'\uff19'
。如果 value 是一個(gè)
tuple
,它應該有三個(gè)組件,一個(gè)符號(0
表示正數或1
表示負數),一個(gè)數字的tuple
和整數指數。 例如,Decimal((0, (1, 4, 1, 4), -3))
返回Decimal('1.414')
。如果 value 是
float
,則二進(jìn)制浮點(diǎn)值無(wú)損地轉換為其精確的十進(jìn)制等效值。 此轉換通常需要53位或更多位數的精度。 例如,Decimal(float('1.1'))
轉換為``Decimal('1.100000000000000088817841970012523233890533447265625')``。context 精度不會(huì )影響存儲的位數。 這完全由 value 中的位數決定。 例如,
Decimal('3.00000')
記錄所有五個(gè)零,即使上下文精度只有三。context 參數的目的是確定 value 是格式錯誤的字符串時(shí)該怎么做。 如果上下文陷阱
InvalidOperation
,則引發(fā)異常;否則,構造函數返回一個(gè)新的 Decimal,其值為NaN
。構造完成后,
Decimal
對象是不可變的。在 3.2 版更改: 現在允許構造函數的參數為
float
實(shí)例。在 3.3 版更改:
float
參數在設置FloatOperation
陷阱時(shí)引發(fā)異常。 默認情況下,陷阱已關(guān)閉。在 3.6 版更改: 允許下劃線(xiàn)進(jìn)行分組,就像代碼中的整數和浮點(diǎn)文字一樣。
十進(jìn)制浮點(diǎn)對象與其他內置數值類(lèi)型共享許多屬性,例如
float
和int
。 所有常用的數學(xué)運算和特殊方法都適用。 同樣,十進(jìn)制對象可以復制、pickle、打印、用作字典鍵、用作集合元素、比較、排序和強制轉換為另一種類(lèi)型(例如float
或int
)。算術(shù)對十進(jìn)制對象和算術(shù)對整數和浮點(diǎn)數有一些小的差別。 當余數運算符
%
應用于Decimal對象時(shí),結果的符號是 被除數 的符號,而不是除數的符號:>>> (-7) % 4 1 >>> Decimal(-7) % Decimal(4) Decimal('-3')
整數除法運算符
//
的行為類(lèi)似,返回真商的整數部分(截斷為零)而不是它的向下取整,以便保留通常的標識x == (x // y) * y + x % y
:>>> -7 // 4 -2 >>> Decimal(-7) // Decimal(4) Decimal('-1')
%
和//
運算符實(shí)現了remainder
和divide-integer
操作(分別),如規范中所述。十進(jìn)制對象通常不能與浮點(diǎn)數或
fractions.Fraction
實(shí)例在算術(shù)運算中結合使用:例如,嘗試將Decimal
加到float
,將引發(fā)TypeError
。 但是,可以使用 Python 的比較運算符來(lái)比較Decimal
實(shí)例x
和另一個(gè)數字y
。 這樣可以避免在對不同類(lèi)型的數字進(jìn)行相等比較時(shí)混淆結果。在 3.2 版更改: 現在完全支持
Decimal
實(shí)例和其他數字類(lèi)型之間的混合類(lèi)型比較。除了標準的數字屬性,十進(jìn)制浮點(diǎn)對象還有許多專(zhuān)門(mén)的方法:
- adjusted()?
在移出系數最右邊的數字之后返回調整后的指數,直到只剩下前導數字:
Decimal('321e+5').adjusted()
返回 7 。 用于確定最高有效位相對于小數點(diǎn)的位置。
- as_integer_ratio()?
返回一對
(n, d)
整數,表示給定的Decimal
實(shí)例作為分數、最簡(jiǎn)形式項并帶有正分母:>>> Decimal('-3.14').as_integer_ratio() (-157, 50)
轉換是精確的。 在 Infinity 上引發(fā) OverflowError ,在 NaN 上引起 ValueError 。
3.6 新版功能.
- as_tuple()?
返回一個(gè) named tuple 表示的數字:
DecimalTuple(sign, digits, exponent)
。
- compare(other, context=None)?
比較兩個(gè) Decimal 實(shí)例的值。
compare()
返回一個(gè) Decimal 實(shí)例,如果任一操作數是 NaN ,那么結果是 NaNa or b is a NaN ==> Decimal('NaN') a < b ==> Decimal('-1') a == b ==> Decimal('0') a > b ==> Decimal('1')
- compare_signal(other, context=None)?
除了所有 NaN 信號之外,此操作與
compare()
方法相同。 也就是說(shuō),如果兩個(gè)操作數都不是信令NaN,那么任何靜默的 NaN 操作數都被視為信令NaN。
- compare_total(other, context=None)?
使用它們的抽象表示而不是它們的數值來(lái)比較兩個(gè)操作數。 類(lèi)似于
compare()
方法,但結果給出了一個(gè)總排序Decimal
實(shí)例。 兩個(gè)Decimal
實(shí)例具有相同的數值但不同的表示形式在此排序中比較不相等:>>> Decimal('12.0').compare_total(Decimal('12')) Decimal('-1')
靜默和發(fā)出信號的 NaN 也包括在總排序中。 這個(gè)函數的結果是
Decimal('0')
如果兩個(gè)操作數具有相同的表示,或是Decimal('-1')
如果第一個(gè)操作數的總順序低于第二個(gè)操作數,或是Decimal('1')
如果第一個(gè)操作數在總順序中高于第二個(gè)操作數。 有關(guān)總排序的詳細信息,請參閱規范。此操作不受上下文影響且靜默:不更改任何標志且不執行舍入。 作為例外,如果無(wú)法準確轉換第二個(gè)操作數,則C版本可能會(huì )引發(fā)InvalidOperation。
- compare_total_mag(other, context=None)?
比較兩個(gè)操作數使用它們的抽象表示而不是它們的值,如
compare_total()
,但忽略每個(gè)操作數的符號。x.compare_total_mag(y)
相當于x.copy_abs().compare_total(y.copy_abs())
。此操作不受上下文影響且靜默:不更改任何標志且不執行舍入。 作為例外,如果無(wú)法準確轉換第二個(gè)操作數,則C版本可能會(huì )引發(fā)InvalidOperation。
- conjugate()?
只返回self,這種方法只符合 Decimal 規范。
- copy_abs()?
返回參數的絕對值。 此操作不受上下文影響并且是靜默的:沒(méi)有更改標志且不執行舍入。
- copy_negate()?
回到參數的否定。 此操作不受上下文影響并且是靜默的:沒(méi)有標志更改且不執行舍入。
- copy_sign(other, context=None)?
返回第一個(gè)操作數的副本,其符號設置為與第二個(gè)操作數的符號相同。 例如:
>>> Decimal('2.3').copy_sign(Decimal('-1.5')) Decimal('-2.3')
此操作不受上下文影響且靜默:不更改任何標志且不執行舍入。 作為例外,如果無(wú)法準確轉換第二個(gè)操作數,則C版本可能會(huì )引發(fā)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)?
Alternative constructor that only accepts instances of
float
orint
.注意, Decimal.from_float(0.1) 與 Decimal('0.1') 不同。 由于 0.1 在二進(jìn)制浮點(diǎn)中不能精確表示,因此該值存儲為最接近的可表示值,即 0x1.999999999999ap-4 。 十進(jìn)制的等效值是`0.1000000000000000055511151231257827021181583404541015625`。
>>> 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 沒(méi)有舍入。
>>> Decimal(2).fma(3, 5) Decimal('11')
- ln(context=None)?
返回操作數的自然對數(以 e 為底)。結果是使用
ROUND_HALF_EVEN
舍入模式正確舍入的。
- log10(context=None)?
返回操作數的以十為底的對數。結果是使用
ROUND_HALF_EVEN
舍入模式正確舍入的。
- logb(context=None)?
對于一個(gè)非零數,返回其運算數的調整后指數作為一個(gè)
Decimal
實(shí)例。 如果運算數為零將返回Decimal('-Infinity')
并且產(chǎn)生 theDivisionByZero
標志。如果運算數是無(wú)限大則返回Decimal('Infinity')
。
- logical_and(other, context=None)?
logical_and()
是需要兩個(gè) 邏輯運算數 的邏輯運算(參考 邏輯操作數 )。按位輸出兩運算數的and
運算的結果。
- logical_invert(context=None)?
logical_invert()
是一個(gè)邏輯運算。結果是操作數的按位求反。
- logical_or(other, context=None)?
logical_or()
是需要兩個(gè) logical operands 的邏輯運算(請參閱 邏輯操作數 )。結果是兩個(gè)運算數的按位的or
運算。
- logical_xor(other, context=None)?
logical_xor()
是需要兩個(gè) 邏輯運算數 的邏輯運算(參考 邏輯操作數 )。結果是按位輸出的兩運算數的異或運算。
- max(other, context=None)?
像
max(self, other)
一樣,除了在返回之前應用上下文舍入規則并且用信號通知或忽略NaN
值(取決于上下文以及它們是發(fā)信號還是安靜)。
- min(other, context=None)?
像
min(self, other)
一樣,除了在返回之前應用上下文舍入規則并且用信號通知或忽略NaN
值(取決于上下文以及它們是發(fā)信號還是安靜)。
- next_minus(context=None)?
返回小于給定操作數的上下文中可表示的最大數字(或者當前線(xiàn)程的上下文中的可表示的最大數字如果沒(méi)有給定上下文)。
- next_plus(context=None)?
返回大于給定操作數的上下文中可表示的最小數字(或者當前線(xiàn)程的上下文中的可表示的最小數字如果沒(méi)有給定上下文)。
- next_toward(other, context=None)?
如果兩運算數不相等,返回在第二個(gè)操作數的方向上最接近第一個(gè)操作數的數。如果兩操作數數值上相等,返回將符號設置為與第二個(gè)運算數相同的第一個(gè)運算數的拷貝。
- normalize(context=None)?
通過(guò)去除尾隨的零并將所有結果等于
Decimal('0')
的轉化為Decimal('0e0')
來(lái)標準化數字。用于為等效類(lèi)的屬性生成規范值。比如,Decimal('32.100')
和Decimal('0.321000e+2')
都被標準化為相同的值Decimal('32.1')
。
- number_class(context=None)?
返回一個(gè)字符串描述運算數的 class 。返回值是以下十個(gè)字符串中的一個(gè)。
"-Infinity"
,指示運算數為負無(wú)窮大。"-Normal"
,指示該運算數是負正常數字。"-Subnormal"
,指示該運算數是負的次標準數。"-Zero"
,指示該運算數是負零。"-Zero"
,指示該運算數是正零。"+Subnormal"
,指示該運算數是正的次標準數。"+Normal"
,指示該運算數是正的標準數。"+Infinity"
,指示該運算數是正無(wú)窮。"NaN"
,指示該運算數是肅靜 NaN (非數字)。"sNaN"
,指示該運算數是信號 NaN 。
- quantize(exp, rounding=None, context=None)?
返回的值等于舍入后的第一個(gè)運算數并且具有第二個(gè)操作數的指數。
>>> Decimal('1.41421356').quantize(Decimal('1.000')) Decimal('1.414')
與其他運算不同,如果量化運算后的系數長(cháng)度大于精度,那么會(huì )發(fā)出一個(gè)
InvalidOperation
信號。這保證了除非有一個(gè)錯誤情況,量化指數恒等于右手運算數的指數。與其他運算不同,量化永不信號下溢,即使結果不正常且不精確。
如果第二個(gè)運算數的指數大于第一個(gè)運算數的指數那或許需要舍入。在這種情況下,舍入模式由給定
rounding
參數決定,其余的由給定context
參數決定;如果參數都未給定,使用當前線(xiàn)程上下文的舍入模式。每當結果的指數大于
Emax
或小于Etiny
就會(huì )返回錯誤。
- remainder_near(other, context=None)?
返回 self 除以 other 的余數。 這與
self % other
的區別在于所選擇的余數要使其絕對值最小化。 更準確地說(shuō),返回值為self - n * other
其中n
是最接近self / other
的實(shí)際值的整數,并且如果兩個(gè)整數與實(shí)際值的差相等則會(huì )選擇其中的偶數。如果結果為零則其符號將為 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)?
返回對第一個(gè)操作數的數碼按第二個(gè)操作數所指定的數量進(jìn)行輪轉的結果。 第二個(gè)操作數必須為 -precision 至 precision 精度范圍內的整數。 第二個(gè)操作數的絕對值給出要輪轉的位數。 如果第二個(gè)操作數為正值則向左輪轉;否則向右輪轉。 如有必要第一個(gè)操作數的系數會(huì )在左側填充零以達到 precision 所指定的長(cháng)度。 第一個(gè)操作數的符號和指數保持不變。
- same_quantum(other, context=None)?
檢測自身與 other 是否具有相同的指數或是否均為
NaN
。此操作不受上下文影響且靜默:不更改任何標志且不執行舍入。 作為例外,如果無(wú)法準確轉換第二個(gè)操作數,則C版本可能會(huì )引發(fā)InvalidOperation。
- scaleb(other, context=None)?
返回第一個(gè)操作數使用第二個(gè)操作數對指數進(jìn)行調整的結果。 等價(jià)于返回第一個(gè)操作數乘以
10**other
的結果。 第二個(gè)操作數必須為整數。
- shift(other, context=None)?
返回第一個(gè)操作數的數碼按第二個(gè)操作數所指定的數量進(jìn)行移位的結果。 第二個(gè)操作數必須為 -precision 至 precision 范圍內的整數。 第二個(gè)操作數的絕對值給出要移動(dòng)的位數。 如果第二個(gè)操作數為正值則向左移位;否則向右移位。 移入系數的數碼為零。 第一個(gè)操作數的符號和指數保持不變。
- sqrt(context=None)?
返回參數的平方根精確到完整精度。
- to_eng_string(context=None)?
轉換為字符串,如果需要指數則會(huì )使用工程標注法。
工程標注法的指數是 3 的倍數。 這會(huì )在十進(jìn)制位的左邊保留至多 3 個(gè)數碼,并可能要求添加一至兩個(gè)末尾零。
例如,此方法會(huì )將
Decimal('123E+1')
轉換為Decimal('1.23E+3')
。
- to_integral(rounding=None, context=None)?
與
to_integral_value()
方法相同。 保留to_integral
名稱(chēng)是為了與舊版本兼容。
邏輯操作數?
logical_and()
, logical_invert()
, logical_or()
和 logical_xor()
方法期望其參數為 邏輯操作數。 邏輯操作數 是指數位與符號位均為零的 Decimal
實(shí)例,并且其數字位均為 0
或 1
。
上下文對象?
上下文是算術(shù)運算所在的環(huán)境。 它們管理精度、設置舍入規則、確定將哪些信號視為異常,并限制指數的范圍。
每個(gè)線(xiàn)程都有自己的當前上下文,可使用 getcontext()
和 setcontext()
函數來(lái)讀取或修改:
- decimal.getcontext()?
返回活動(dòng)線(xiàn)程的當前上下文。
- decimal.setcontext(c)?
將活動(dòng)線(xiàn)程的當前上下文設為 c。
你也可以使用 with
語(yǔ)句和 localcontext()
函數來(lái)臨時(shí)改變活動(dòng)上下文。
- decimal.localcontext(ctx=None, **kwargs)?
Return a context manager that will set the current context for the active thread to a copy of ctx on entry to the with-statement and restore the previous context when exiting the with-statement. If no context is specified, a copy of the current context is used. The kwargs argument is used to set the attributes of the new context.
例如,以下代碼會(huì )將當前 decimal 精度設為 42 位,執行一個(gè)運算,然后自動(dòng)恢復之前的上下文:
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
Using keyword arguments, the code would be the following:
from decimal import localcontext with localcontext(prec=42) as ctx: s = calculate_something() s = +s
Raises
TypeError
if kwargs supplies an attribute thatContext
doesn't support. Raises eitherTypeError
orValueError
if kwargs supplies an invalid value for an attribute.在 3.11 版更改:
localcontext()
now supports setting context attributes through the use of keyword arguments.
新的上下文也可使用下述的 Context
構造器來(lái)創(chuàng )建。 此外,模塊還提供了三種預設的上下文:
- class decimal.BasicContext?
這是由通用十進(jìn)制算術(shù)規范描述所定義的標準上下文。 精度設為九。 舍入設為
ROUND_HALF_UP
。 清除所有旗標。 啟用所有陷阱(視為異常),但Inexact
,Rounded
和Subnormal
除外。由于啟用了許多陷阱,此上下文適用于進(jìn)行調試。
- class decimal.ExtendedContext?
這是由通用十進(jìn)制算術(shù)規范描述所定義的標準上下文。 精度設為九。 舍入設為
ROUND_HALF_EVEN
。 清除所有旗標。 不啟用任何陷阱(因此在計算期間不會(huì )引發(fā)異常)。由于禁用了陷阱,此上下文適用于希望結果值為
NaN
或Infinity
而不是引發(fā)異常的應用。 這允許應用在出現當其他情況下會(huì )中止程序的條件時(shí)仍能完成運行。
- class decimal.DefaultContext?
此上下文被
Context
構造器用作新上下文的原型。 改變一個(gè)字段(例如精度)的效果將是改變Context
構造器所創(chuàng )建的新上下文的默認值。此上下文最適用于多線(xiàn)程環(huán)境。 在線(xiàn)程開(kāi)始前改變一個(gè)字段具有設置全系統默認值的效果。 不推薦在線(xiàn)程開(kāi)始后改變字段,因為這會(huì )要求線(xiàn)程同步避免競爭條件。
在單線(xiàn)程環(huán)境中,最好完全不使用此上下文。 而是簡(jiǎn)單地電顯式創(chuàng )建上下文,具體如下所述。
默認值為
prec
=28
,rounding
=ROUND_HALF_EVEN
,并為Overflow
,InvalidOperation
和DivisionByZero
啟用陷阱。
在已提供的三種上下文之外,還可以使用 Context
構造器創(chuàng )建新的上下文。
- class decimal.Context(prec=None, rounding=None, Emin=None, Emax=None, capitals=None, clamp=None, flags=None, traps=None)?
創(chuàng )建一個(gè)新上下文。 如果某個(gè)字段未指定或為
None
,則從DefaultContext
拷貝默認值。 如果 flags 字段未指定或為None
,則清空所有旗標。prec 為一個(gè) [
1
,MAX_PREC
] 范圍內的整數,用于設置該上下文中算術(shù)運算的精度。rounding 選項應為 Rounding Modes 小節中列出的常量之一。
traps 和 flags 字段列出要設置的任何信號。 通常,新上下文應當只設置 traps 而讓 flags 為空。
Emin 和 Emax 字段給定指數所允許的外部上限。 Emin 必須在 [
MIN_EMIN
,0
] 范圍內,Emax 在 [0
,MAX_EMAX
] 范圍內。capitals 字段為
0
或1
(默認值)。 如果設為1
,指數將附帶打印大寫(xiě)的E
;其他情況則將使用小寫(xiě)的e
:Decimal('6.02e+23')
。clamp 字段為
0
(默認值) 或1
。 如果設為1
,則Decimal
實(shí)例的指數e
的表示范圍在此上下文中將嚴格限制為Emin - prec + 1 <= e <= Emax - prec + 1
。 如果 clamp 為0
則將適用較弱的條件:Decimal
實(shí)例調整后的指數最大值為Emax
。 當 clamp 為1
時(shí),一個(gè)較大的普通數值將在可能的情況下減小其指數并為其系統添加相應數量的零,以便符合指數值限制;這可以保持數字值但會(huì )丟失有效末尾零的信息。 例如:>>> Context(prec=6, Emax=999, clamp=1).create_decimal('1.23e999') Decimal('1.23000E+999')
clamp 值為
1
時(shí)即允許與在 IEEE 754 中描述的固定寬度十進(jìn)制交換格式保持兼容性。Context
類(lèi)定義了幾種通用方法以及大量直接在給定上下文中進(jìn)行算術(shù)運算的方法。 此外,對于上述的每種Decimal
方法(不包括adjusted()
和as_tuple()
方法)都有一個(gè)相應的Context
方法。 例如,對于一個(gè)Context
的實(shí)例C
和Decimal
的實(shí)例x
,C.exp(x)
就等價(jià)于x.exp(context=C)
。 每個(gè)Context
方法都接受一個(gè) Python 整數(即int
的實(shí)例)在任何接受 Decimal 的實(shí)例的地方使用。- clear_flags()?
將所有旗標重置為
0
。
- clear_traps()?
將所有陷阱重置為零
0
。3.3 新版功能.
- copy()?
返回上下文的一個(gè)副本。
- copy_decimal(num)?
返回 Decimal 實(shí)例 num 的一個(gè)副本。
- create_decimal(num)?
基于 num 創(chuàng )建一個(gè)新 Decimal 實(shí)例但使用 self 作為上下文。 與
Decimal
構造器不同,該上下文的精度、舍入方法、旗標和陷阱會(huì )被應用于轉換過(guò)程。此方法很有用處,因為常量往往被給予高于應用所需的精度。 另一個(gè)好處在于立即執行舍入可以消除超出當前精度的數位所導致的意外效果。 在下面的示例中,使用未舍入的輸入意味著(zhù)在總和中添加零會(huì )改變結果:
>>> getcontext().prec = 3 >>> Decimal('3.4445') + Decimal('1.0023') Decimal('4.45') >>> Decimal('3.4445') + Decimal(0) + Decimal('1.0023') Decimal('4.44')
此方法實(shí)現了 IBM 規格描述中的轉換為數字操作。 如果參數為字符串,則不允許有開(kāi)頭或末尾的空格或下劃線(xiàn)。
- create_decimal_from_float(f)?
基于浮點(diǎn)數 f 創(chuàng )建一個(gè)新的 Decimal 實(shí)例,但會(huì )使用 self 作為上下文來(lái)執行舍入。 與
Decimal.from_float()
類(lèi)方法不同,上下文的精度、舍入方法、旗標和陷阱會(huì )應用到轉換中。>>> 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 新版功能.
- Etop()?
返回一個(gè)等于
Emax - prec + 1
的值。
使用 decimal 的通常方式是創(chuàng )建
Decimal
實(shí)例然后對其應用算術(shù)運算,這些運算發(fā)生在活動(dòng)線(xiàn)程的當前上下文中。 一種替代方式則是使用上下文的方法在特定上下文中進(jìn)行計算。 這些方法類(lèi)似于Decimal
類(lèi)的方法,在此僅簡(jiǎn)單地重新列出。- abs(x)?
返回 x 的絕對值。
- add(x, y)?
返回 x 與 y 的和。
- canonical(x)?
返回相同的 Decimal 對象 x。
- compare(x, y)?
對 x 與 y 進(jìn)行數值比較。
- compare_signal(x, y)?
對兩個(gè)操作數進(jìn)行數值比較。
- compare_total(x, y)?
對兩個(gè)操作數使用其抽象表示進(jìn)行比較。
- compare_total_mag(x, y)?
對兩個(gè)操作數使用其抽象表示進(jìn)行比較,忽略符號。
- 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)?
兩個(gè)數字相除并返回結果的整數部分。
- 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 是無(wú)限的則返回
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)?
在操作數的每個(gè)數位間應用邏輯運算 and。
- logical_invert(x)?
反轉 x 中的所有數位。
- logical_or(x, y)?
在操作數的每個(gè)數位間應用邏輯運算 or。
- logical_xor(x, y)?
在操作數的每個(gè)數位間應用邏輯運算 xor。
- max(x, y)?
對兩個(gè)值執行數字比較并返回其中的最大值。
- max_mag(x, y)?
對兩個(gè)值執行忽略正負號的數字比較。
- min(x, y)?
對兩個(gè)值執行數字比較并返回其中的最小值。
- min_mag(x, y)?
對兩個(gè)值執行忽略正負號的數字比較。
- minus(x)?
對應于 Python 中的單目前綴取負運算符執行取負操作。
- multiply(x, y)?
返回 x 和 y 的積。
- next_minus(x)?
返回小于 x 的最大數字表示形式。
- next_plus(x)?
返回大于 x 的最小數字表示形式。
- next_toward(x, y)?
返回 x 趨向于 y 的最接近的數字。
- normalize(x)?
將 x 改寫(xiě)為最簡(jiǎn)形式。
- number_class(x)?
返回 x 的類(lèi)的表示。
- plus(x)?
對應于 Python 中的單目前綴取正運算符執行取正操作。 此操作將應用上下文精度和舍入,因此它 不是 標識運算。
- power(x, y, modulo=None)?
返回
x
的y
次方,如果給出了模數modulo
則取其余數。如為兩個(gè)參數則計算
x**y
。 如果x
為負值則y
必須為整數。 除非y
為整數且結果為有限值并可在 'precision' 位內精確表示否則結果將是不精確的。 上下文的舍入模式將被使用。 結果在 Python 版中總是會(huì )被正確地舍入。Decimal(0) ** Decimal(0)
結果為InvalidOperation
,而如果InvalidOperation
未被捕獲,則結果為Decimal('NaN')
。在 3.3 版更改: C 模塊計算
power()
時(shí)會(huì )使用已正確舍入的exp()
和ln()
函數。 結果是經(jīng)過(guò)良好定義的,但僅限于“幾乎總是正確地舍入”。帶有三個(gè)參數時(shí),計算
(x**y) % modulo
。 對于三個(gè)參數的形式,參數將會(huì )應用以下限制:三個(gè)參數必須都是整數
y
必須是非負數x
或y
至少有一個(gè)不為零modulo
必須不為零且至多有 'precision' 位
來(lái)自
Context.power(x, y, modulo)
的結果值等于使用無(wú)限精度計算(x**y) % modulo
所得到的值,但其計算過(guò)程更高效。 結果的指數為零,無(wú)論x
,y
和modulo
的指數是多少。 結果值總是完全精確的。
- quantize(x, y)?
返回的值等于 x (舍入后),并且指數為 y。
- radix()?
恰好返回 10,因為這是 Decimal 對象 :)
- remainder(x, y)?
返回整除所得到的余數。
結果的符號,如果不為零,則與原始除數的符號相同。
- remainder_near(x, y)?
返回
x - y * n
,其中 n 為最接近x / y
實(shí)際值的整數(如結果為 0 則其符號將與 x 的符號相同)。
- rotate(x, y)?
返回 x 翻轉 y 次的副本。
- same_quantum(x, y)?
如果兩個(gè)操作數具有相同的指數則返回
True
。
- scaleb(x, y)?
返回第一個(gè)操作數添加第二個(gè)值的指數后的結果。
- shift(x, y)?
返回 x 變換 y 次的副本。
- sqrt(x)?
非負數基于上下文精度的平方根。
- subtract(x, y)?
返回 x 和 y 的差。
- to_eng_string(x)?
轉換為字符串,如果需要指數則會(huì )使用工程標注法。
工程標注法的指數是 3 的倍數。 這會(huì )在十進(jìn)制位的左邊保留至多 3 個(gè)數碼,并可能要求添加一至兩個(gè)末尾零。
- to_integral_exact(x)?
舍入到一個(gè)整數。
- to_sci_string(x)?
使用科學(xué)計數法將一個(gè)數字轉換為字符串。
常量?
本節中的常量?jì)H與 C 模塊相關(guān)。 它們也被包含在純 Python 版本以保持兼容性。
32位 |
64位 |
|
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
- decimal.HAVE_THREADS?
該值為
True
。 已棄用,因為 Python 現在總是啟用線(xiàn)程。
3.9 版后已移除.
- decimal.HAVE_CONTEXTVAR?
默認值為
True
。 如果 Python 編譯版本使用了 --without-decimal-contextvar 選項來(lái)配置
,則 C 版本會(huì )使用線(xiàn)程局部而非協(xié)程局部上下文并且該值為False
。 這在某些嵌套上下文場(chǎng)景中將會(huì )稍快一些。
3.9 新版功能: 向下移植到 3.7 和 3.8。
舍入模式?
- 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 則舍入到零的反方向;否則舍入方向為零。
信號?
信號代表在計算期間引發(fā)的條件。 每個(gè)信號對應于一個(gè)上下文旗標和一個(gè)上下文陷阱啟用器。
上下文旗標將在遇到特定條件時(shí)被設定。 在完成計算之后,將為了獲得信息而檢測旗標(例如確定計算是否精確)。 在檢測旗標后,請確保在開(kāi)始下一次計算之前清除所有旗標。
如果為信號設定了上下文的陷阱啟用器,則條件會(huì )導致特定的 Python 異常被引發(fā)。 舉例來(lái)說(shuō),如果設定了 DivisionByZero
陷阱,則當遇到此條件時(shí)就將引發(fā) DivisionByZero
異常。
- class decimal.Clamped?
修改一個(gè)指數以符合表示限制。
通常,限位將在一個(gè)指數超出上下文的
Emin
和Emax
限制時(shí)發(fā)生。 在可能的情況下,會(huì )通過(guò)給系數添加零來(lái)將指數縮減至符合限制。
- class decimal.DecimalException?
其他信號的基類(lèi),并且也是
ArithmeticError
的一個(gè)子類(lèi)。
- class decimal.DivisionByZero?
非無(wú)限數被零除的信號。
可在除法、取余隊法或對一個(gè)數求負數次冪時(shí)發(fā)生。 如果此信號未被陷阱捕獲,則返回
Infinity
或-Infinity
并且由對計算的輸入來(lái)確定正負符號。
- class decimal.Inexact?
表明發(fā)生了舍入且結果是不精確的。
有非零數位在舍入期間被丟棄的信號。 舍入結果將被返回。 此信號旗標或陷阱被用于檢測結果不精確的情況。
- class decimal.InvalidOperation?
執行了一個(gè)無(wú)效的操作。
表明請求了一個(gè)無(wú)意義的操作。 如未被陷阱捕獲則返回
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?
數值的溢出。
表明在發(fā)生舍入之后的指數大于
Emax
。 如果未被陷阱捕獲,則結果將取決于舍入模式,或者向下舍入為最大的可表示有限數,或者向上舍入為Infinity
。 無(wú)論哪種情況,都將引發(fā)Inexact
和Rounded
信號。
- class decimal.Rounded?
發(fā)生了舍入,但或許并沒(méi)有信息丟失。
一旦舍入丟棄了數位就會(huì )發(fā)出此信號;即使被丟棄的數位是零 (例如將
5.00
舍入為5.0
)。 如果未被陷阱捕獲,則不經(jīng)修改地返回結果。 此信號用于檢測有效位數的丟棄。
- class decimal.Subnormal?
在舍入之前指數低于
Emin
。當操作結果是次標準數(即指數過(guò)?。r(shí)就會(huì )發(fā)出此信號。 如果未被陷阱捕獲,則不經(jīng)修改過(guò)返回結果。
- class decimal.Underflow?
數字向下溢出導致結果舍入到零。
當一個(gè)次標準數結果通過(guò)舍入轉為零時(shí)就會(huì )發(fā)出此信號。 同時(shí)還將引發(fā)
Inexact
和Subnormal
信號。
- class decimal.FloatOperation?
為 float 和 Decimal 的混合啟用更嚴格的語(yǔ)義。
如果信號未被捕獲(默認),則在
Decimal
構造器、create_decimal()
和所有比較運算中允許 float 和 Decimal 的混合。 轉換和比較都是完全精確的。 發(fā)生的任何混合運算都將通過(guò)在上下文旗標中設置FloatOperation
來(lái)靜默地記錄。 通過(guò)from_float()
或create_decimal_from_float()
進(jìn)行顯式轉換則不會(huì )設置旗標。在其他情況下(即信號被捕獲),則只靜默執行相等性比較和顯式轉換。 所有其他混合運算都將引發(fā)
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)
浮點(diǎn)數說(shuō)明?
通過(guò)提升精度來(lái)解決舍入錯誤?
使用十進(jìn)制浮點(diǎn)數可以消除十進(jìn)制表示錯誤(即能夠完全精確地表示 0.1
這樣的數);然而,某些運算在非零數位超出給定的精度時(shí)仍然可能導致舍入錯誤。
舍入錯誤的影響可能因接近相互抵銷(xiāo)的加減運算被放大從而導致丟失有效位。 Knuth 提供了兩個(gè)指導性示例,其中出現了精度不足的浮點(diǎn)算術(shù)舍入,導致加法的交換律和分配律被打破:
# 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
模塊則可以通過(guò)充分地擴展精度來(lái)避免有效位的丟失:
>>> 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
模塊的數字系統提供了一些特殊的值,包括 NaN
, sNaN
, -Infinity
, Infinity
以及兩種零值 +0
和 -0
。
無(wú)窮大可以使用 Decimal('Infinity')
來(lái)構建。 它們也可以在不捕獲 DivisionByZero
信號捕獲時(shí)通過(guò)除以零來(lái)產(chǎn)生。 類(lèi)似地,當不捕獲 Overflow
信號時(shí),也可以通過(guò)舍入到超出最大可表示數字限制的方式產(chǎn)生無(wú)窮大的結果。
無(wú)窮大是有符號的(仿射)并可用于算術(shù)運算,它們會(huì )被當作極其巨大的不確定數字來(lái)處理。 例如,無(wú)窮大加一個(gè)常量結果也將為無(wú)窮大。
某些不存在有效結果的運算將會(huì )返回 NaN
,或者如果捕獲了 InvalidOperation
信號則會(huì )引發(fā)一個(gè)異常。 例如,0/0
會(huì )返回 NaN
表示結果“不是一個(gè)數字”。 這樣的 NaN
是靜默產(chǎn)生的,并且在產(chǎn)生之后參與其它計算時(shí)總是會(huì )得到 NaN
的結果。 這種行為對于偶而缺少輸入的各類(lèi)計算都很有用處 --- 它允許在將特定結果標記為無(wú)效的同時(shí)讓計算繼續運行。
另一種變體形式是 sNaN
,它在每次運算后會(huì )發(fā)出信號而不是保持靜默。 當對于無(wú)效結果需要中斷計算進(jìn)行特別處理時(shí),這是一個(gè)很有用的返回值。
Python 中比較運算符的行為在涉及 NaN
時(shí)可能會(huì )令人有點(diǎn)驚訝。 相等性檢測在操作數中有靜默型或信號型 NaN
時(shí)總是會(huì )返回 False
(即使是執行 Decimal('NaN')==Decimal('NaN')
),而不等性檢測總是會(huì )返回 True
。 當嘗試使用 <
, <=
, >
或 >=
運算符中的任何一個(gè)來(lái)比較兩個(gè) Decimal 值時(shí),如果運算數中有 NaN
則將引發(fā) InvalidOperation
信號,如果此信號未被捕獲則將返回 False
。 請注意通用十進(jìn)制算術(shù)規范并未規定直接比較行為;這些涉及 NaN
的比較規則來(lái)自于 IEEE 854 標準 (見(jiàn)第 5.7 節表 3)。 要確保嚴格符合標準,請改用 compare()
和 compare-signal()
方法。
有符號零值可以由向下溢出的運算產(chǎn)生。 它們保留符號是為了讓運算結果能以更高的精度傳遞。 由于它們的大小為零,正零和負零會(huì )被視為相等,且它們的符號具有信息。
在這兩個(gè)不相同但卻相等的有符號零之外,還存在幾種零的不同表示形式,它們的精度不同但值也都相等。 這需要一些時(shí)間來(lái)逐漸適應。 對于習慣了標準浮點(diǎn)表示形式的眼睛來(lái)說(shuō),以下運算返回等于零的值并不是顯而易見(jiàn)的:
>>> 1 / Decimal('Infinity')
Decimal('0E-1000026')
使用線(xiàn)程?
getcontext()
函數會(huì )為每個(gè)線(xiàn)程訪(fǎng)問(wèn)不同的 Context
對象。 具有單獨線(xiàn)程上下文意味著(zhù)線(xiàn)程可以修改上下文 (例如 getcontext().prec=10
) 而不影響其他線(xiàn)程。
類(lèi)似的 setcontext()
會(huì )為當前上下文的目標自動(dòng)賦值。
如果在調用 setcontext()
之前調用了 getcontext()
,則 getcontext()
將自動(dòng)創(chuàng )建一個(gè)新的上下文在當前線(xiàn)程中使用。
新的上下文拷貝自一個(gè)名為 DefaultContext 的原型上下文。 要控制默認值以便每個(gè)線(xiàn)程在應用運行期間都使用相同的值,可以直接修改 DefaultContext 對象。 這應當在任何線(xiàn)程啟動(dòng) 之前 完成以使得調用 getcontext()
的線(xiàn)程之間不會(huì )產(chǎn)生競爭條件。 例如:
# 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
類(lèi)的各種方式:
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 常見(jiàn)問(wèn)題?
Q. 總是輸入 decimal.Decimal('1234.5')
是否過(guò)于笨拙。 在使用交互解釋器時(shí)有沒(méi)有最小化輸入量的方式?
A. 有些用戶(hù)會(huì )將構造器簡(jiǎn)寫(xiě)為一個(gè)字母:
>>> D = decimal.Decimal
>>> D('1.23') + D('3.45')
Decimal('4.68')
Q. 在帶有兩個(gè)十進(jìn)制位的定點(diǎn)數應用中,有些輸入值具有許多位,需要被舍入。 另一些數則不應具有多余位,需要驗證有效性。 這種情況應該用什么方法?
A. 用 quantize()
方法舍入到固定數量的十進(jìn)制位。 如果設置了 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
Q. 當我使用兩個(gè)有效位的輸入時(shí),我要如何在一個(gè)應用中保持有效位不變?
A. 某些運算例如與整數相加、相減和相乘將會(huì )自動(dòng)保留固定的小數位數。 其他運算,例如相除和非整數相乘則將會(huì )改變小數位數,需要再加上 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')
在開(kāi)發(fā)定點(diǎn)數應用時(shí),更方便的做法是定義處理 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')
Q. 表示同一個(gè)值有許多方式。 數字 200
, 200.000
, 2E2
和 02E+4
的值都相同但有精度不同。 是否有辦法將它們轉換為一個(gè)可識別的規范值?
A. 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')]
Q. 有些十進(jìn)制值總是被打印為指數表示形式。 是否有辦法得到一個(gè)非指數表示形式?
A. 對于某些值來(lái)說(shuō),指數表示形式是表示系數中有效位的唯一辦法。 例如,將 5.0E+3
表示為 5000
可以讓值保持恒定,但是無(wú)法顯示原本的兩位有效數字。
如果一個(gè)應用不必關(guān)心追蹤有效位,則可以很容易地移除指數和末尾的零,丟棄有效位但讓值保持不變:
>>> def remove_exponent(d):
... return d.quantize(Decimal(1)) if d == d.to_integral() else d.normalize()
>>> remove_exponent(Decimal('5E+3'))
Decimal('5000')
Q. 是否有辦法將一個(gè)普通浮點(diǎn)數轉換為 Decimal
?
A. 是的,任何二進(jìn)制浮點(diǎn)數都可以精確地表示為 Decimal 值,但完全精確的轉換可能需要比平常感覺(jué)更高的精度:
>>> Decimal(math.pi)
Decimal('3.141592653589793115997963468544185161590576171875')
Q. 在一個(gè)復雜的計算中,我怎樣才能保證不會(huì )得到由精度不足和舍入異常所導致的虛假結果。
A. 使用 decimal 模塊可以很容易地檢測結果。 最好的做法是使用更高的精度和不同的舍入模式重新進(jìn)行計算。 明顯不同的結果表明存在精度不足、舍入模式問(wèn)題、不符合條件的輸入或是結果不穩定的算法。
Q. 我發(fā)現上下文精度的應用只針對運算結果而不針對輸入。在混合使用不同精度的值時(shí)有什么需要注意的嗎?
A. 是的。 原則上所有值都會(huì )被視為精確值,在這些值上進(jìn)行的算術(shù)運算也是如此。 只有結果會(huì )被舍入。 對于輸入來(lái)說(shuō)其好處是“所輸入即所得”。 而其缺點(diǎn)則是如果你忘記了輸入沒(méi)有被舍入,結果看起來(lái)可能會(huì )很奇怪:
>>> 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()
方法在創(chuàng )建輸入時(shí)執行舍入:
>>> Context(prec=5, rounding=ROUND_DOWN).create_decimal('1.2345678')
Decimal('1.2345')
Q. CPython 實(shí)現對于巨大數字是否足夠快速?
A. 是的。 在 CPython 和 PyPy3 實(shí)現中,decimal 模塊的 C/CFFI 版本集成了高速 libmpdec 庫用于實(shí)現任意精度正確舍入的十進(jìn)制浮點(diǎn)算術(shù) 1。 libmpdec
會(huì )對中等大小的數字使用 Karatsuba 乘法 而對非常巨大的數字使用 數字原理變換。
必須要對任意精度算術(shù)適配上下文。 Emin
和 Emax
應當總是設為最大值,clamp
應當總是設為 0 (默認值)。 設置 prec
需要十分謹慎。
進(jìn)行大數字算術(shù)的最便捷方式也是使用 prec
的最大值 2:
>>> setcontext(Context(prec=MAX_PREC, Emax=MAX_EMAX, Emin=MIN_EMIN))
>>> x = Decimal(2) ** 256
>>> x / 128
Decimal('904625697166532776746648320380374280103671755200316906558262375061821325312')
對于不精確的結果,在 64 位平臺上 MAX_PREC
的值太大了,可用的內存將會(huì )不足:
>>> Decimal(1) / 3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
MemoryError
在具有超量分配的系統上 (即 Linux),一種更復雜的方式根據可用的 RAM 大小來(lái)調整 prec
。 假設你有 8GB 的 RAM 并期望同時(shí)有 10 個(gè)操作數,每個(gè)最多使用 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'>]
總體而言(特別是在沒(méi)有超量分配的系統上),如果期望所有計算都是精確的則推薦預估更嚴格的邊界并設置 Inexact
陷阱。