-
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