-
Decimal 類型的“設計是基於考慮人類習慣的浮點數模型,並且因此具有以下最高指導原則 —— 計算機必須提供與人們在學校所學習的算術相一致的算術。” —— 摘自 decimal 算術規範描述。
-
Decimal 數字的表示是精確的。 相比之下,1.1 和 2.2 這樣則是不精確的二進制浮點數表示。 最終用戶通常不希望 1.1 + 2.2 的結果會如採用二進制浮點數時那樣顯示爲 3.3000000000000003。
-
精確性會延續到算術類操作中。 對於 decimal 浮點數,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')
-
二進制和 decimal 浮點數都是根據已發佈的標準實現的。 雖然內置浮點類型只公開其功能的一小部分,但 decimal 模塊公開了標準的所有必需部分。 在需要時,程序員可以完全控制舍入和信號處理。 這包括通過使用異常來阻止任何不精確操作來強制執行精確算術的選項。
-
decimal 模塊旨在支持“無偏差,精確無舍入的十進制算術(有時稱爲定點數算術)和有舍入的浮點數算術”。 —— 摘自 decimal 算術規範說明。
快速入門教程
>>> from decimal import *
>>> getcontext()
Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[InvalidOperation, DivisionByZero, Overflow])
>>> getcontext().prec = 7 # Set a new precision
可以基於整數、字符串、浮點數或元組構造 Decimal 實例。 基於整數或浮點數構造將執行該整數或浮點值的精確轉換。 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 新版功能.
>>> 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 版更改.
>>> 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() 函數訪問當前上下文並允許更改設置。 這種方法滿足大多數應用程序的需求。
>>> 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=[])
>>> 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 對象
class decimal.
Decimal
(value="0", context=None)
sign ::= '+' | '-'digit ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'indicator ::= 'e' | 'E'digits ::= digit [digit]...decimal-part ::= digits '.' [digits] | ['.'] digitsexponent-part ::= indicator [sign] digitsinfinity ::= 'Infinity' | 'Inf'nan ::= 'NaN' [digits] | 'sNaN' [digits]numeric-value ::= decimal-part [exponent-part] | infinitynumeric-string ::= [sign] numeric-value | [sign] nan
adjusted
()
as_integer_ratio
()
as_tuple
()
canonical
()
compare
(other, context=None)
compare_signal
(other, context=None)
compare_total
(other, context=None)
compare_total_mag
(other, context=None)
conjugate
()
copy_abs
()
copy_negate
()
copy_sign
(other, context=None)
exp
(context=None)
>>> Decimal(1).exp()Decimal('2.718281828459045235360287471')>>> Decimal(321).exp()Decimal('2.561702493119680037517373933E+139')
from_float
(f)
>>> 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')
fma
(other, third, context=None)
>>> Decimal(2).fma(3, 5)Decimal('11')
is_canonical
()
is_finite
()
is_infinite
()
is_nan
()
is_normal
(context=None)
is_qnan
()
is_signed
()
is_snan
()
is_subnormal
(context=None)
is_zero
()
ln
(context=None)
log10
(context=None)
logb
(context=None)
logical_and
(other, context=None)
logical_invert
(context=None)
logical_or
(other, context=None)
logical_xor
(other, context=None)
max
(other, context=None)
max_mag
(other, context=None)
min
(other, context=None)
min_mag
(other, context=None)
next_minus
(context=None)
next_plus
(context=None)
next_toward
(other, context=None)
normalize
(context=None)
number_class
(context=None)
-
"-Infinity" ,指示操作數爲負無窮大。
-
"-Normal" ,指示該操作數是負正常數字。
-
"-Subnormal" ,指示該操作數是負的次正規數。
-
"-Zero" ,指示該操作數是負零。
-
"-Zero" ,指示該操作數是正零。
-
"+Subnormal" ,指示該操作數是正的次正規數。
-
"+Normal" ,指示該操作數是正的正規數。
-
"+Infinity" ,指示該運算數是正無窮。
-
"NaN" ,指示該運算數是沉寂的 NaN (非數字)。
-
"sNaN" ,指示該運算數是信號 NaN 。
quantize
(exp, rounding=None, context=None)
radix
()
remainder_near
(other, context=None)
rotate
(other, context=None)
same_quantum
(other, context=None)
scaleb
(other, context=None)
shift
(other, context=None)
sqrt
(context=None)
to_eng_string
(context=None)
to_integral
(rounding=None, context=None)
to_integral_exact
(rounding=None, context=None)
to_integral_value
(rounding=None, context=None)
邏輯操作數
Context 對象
decimal.
getcontext
()
decimal.
setcontext
(c)
decimal.
localcontext
(ctx=None)
class decimal.
BasicContext
class decimal.
ExtendedContext
class decimal.
DefaultContext
class decimal.
Context
(prec=None, rounding=None, Emin=None, Emax=None, capitals=None, clamp=None, flags=None, traps=None)
clear_flags
()
clear_traps
()
copy
()
copy_decimal
(num)
create_decimal
(num)
create_decimal_from_float
(f)
Etiny
()
Etop
()
abs
(x)
add
(x, y)
canonical
(x)
compare
(x, y)
compare_signal
(x, y)
compare_total
(x, y)
compare_total_mag
(x, y)
copy_abs
(x)
copy_negate
(x)
copy_sign
(x, y)
divide
(x, y)
divide_int
(x, y)
divmod
(x, y)
exp
(x)
fma
(x, y, z)
is_canonical
(x)
is_finite
(x)
is_infinite
(x)
is_nan
(x)
is_normal
(x)
is_qnan
(x)
is_signed
(x)
is_snan
(x)
is_subnormal
(x)
is_zero
(x)
ln
(x)
log10
(x)
logb
(x)
logical_and
(x, y)
logical_invert
(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)
multiply
(x, y)
next_minus
(x)
next_plus
(x)
next_toward
(x, y)
normalize
(x)
number_class
(x)
plus
(x)
power
(x, y, modulo=None)
三個參數必須都是整數 y 必須是非負數 x 或 y 至少有一個不爲零 modulo 必須不爲零且至多有 ‘precision’ 位
quantize
(x, y)
radix
()
remainder
(x, y)
remainder_near
(x, y)
rotate
(x, y)
same_quantum
(x, y)
scaleb
(x, y)
shift
(x, y)
sqrt
(x)
subtract
(x, y)
to_eng_string
(x)
to_integral_exact
(x)
to_sci_string
(x)
常量
32位
|
64位
|
|
|
425000000
|
999999999999999999
|
|
425000000
|
999999999999999999
|
|
-425000000
|
-999999999999999999
|
|
-849999999
|
-1999999999999999997
|
decimal.
HAVE_THREADS
舍入模式
decimal.
ROUND_CEILING
decimal.
ROUND_DOWN
decimal.
ROUND_FLOOR
decimal.
ROUND_HALF_DOWN
decimal.
ROUND_HALF_EVEN
decimal.
ROUND_HALF_UP
decimal.
ROUND_UP
decimal.
ROUND_05UP
9.4.6. 信號
class decimal.
Clamped
class decimal.
DecimalException
class decimal.
DivisionByZero
class decimal.
Inexact
class decimal.
InvalidOperation
class decimal.
Overflow
class decimal.
Rounded
class decimal.
Subnormal
class decimal.
Underflow
class decimal.
FloatOperation
浮點數說明
通過提升精度來緩解舍入誤差
>>> from decimal import Decimal, getcontext>>> getcontext().prec = 8>>> u, v, w = Decimal(11111113), Decimal(-11111111), Decimal('7.51111111')>>> (u + v) + wDecimal('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')
>>> getcontext().prec = 20>>> u, v, w = Decimal(11111113), Decimal(-11111111), Decimal('7.51111111')>>> (u + v) + wDecimal('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')
特殊的值
使用線程
# Set applicationwide defaults for all threads about to be launchedDefaultContext.prec = 12DefaultContext.rounding = ROUND_DOWNDefaultContext.traps = ExtendedContext.traps.copy()DefaultContext.traps[InvalidOperation] = 1setcontext(DefaultContext)# Afterwards, the threads can be startedt1.start()t2.start()t3.start(). . .
例程
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 FAQ
>>> D = decimal.Decimal>>> D('1.23') + D('3.45')Decimal('4.68')
>>> 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
>>> a = Decimal('102.72') # Initial fixed-point values>>> b = Decimal('3.17')>>> a + b # Addition preserves fixed-pointDecimal('105.89')>>> a - bDecimal('99.55')>>> a * 42 # So does integer multiplicationDecimal('4314.24')>>> (a * b).quantize(TWOPLACES) # Must quantize non-integer multiplicationDecimal('325.62')>>> (b / a).quantize(TWOPLACES) # And quantize divisionDecimal('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-pointDecimal('325.62')>>> div(b, a)Decimal('0.03')
>>> 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')]
>>> 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(math.pi)Decimal('3.141592653589793115997963468544185161590576171875')
>>> 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 roundingDecimal('1.23')此外,還可以使用 Context.create_decimal() 方法在創建輸入時執行舍入:>>> Context(prec=5, rounding=ROUND_DOWN).create_decimal('1.2345678')Decimal('1.2345