Python标准库参考笔记-decimal
10.4 decimal
Decimal支持大多数的数学操作。使用decimal的时候是在一个context背景下工作的。可以使用getcontext来获得当前背景:
from decimal import *
c = getcontext()
print c
结果:
Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999999, Emax=999999999, capitals=1, flags=[], traps=[Overflow, InvalidOperation, DivisionByZero])
使用Decimal,int类型可以直接用来构造Decimal,但是float类型的变量要先转换为字符串。在进行运算之后结果会使用getcontext().prec来确定精度,例如prec为5的时候,100.1234567890+0会等于100.12:
# -*- coding: cp936 -*-
from decimal import *
con = getcontext()
print '-----------------------context------------------------'
print con
a = Decimal(100)
print a
b = Decimal('100.001')
print b
print '--------------------------prec------------------------------'
c = Decimal('100.1234567890')
print c
con.prec = 5
print con
d = Decimal('100.1234567890')
print 'd:',d
print 'd+0:',d+0
结果:
>>>
-----------------------context------------------------
Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999999, Emax=999999999, capitals=1, flags=[], traps=[DivisionByZero, InvalidOperation, Overflow])
100
100.001
--------------------------prec------------------------------
100.1234567890
Context(prec=5, rounding=ROUND_HALF_EVEN, Emin=-999999999, Emax=999999999, capitals=1, flags=[], traps=[DivisionByZero, InvalidOperation, Overflow])
d: 100.1234567890
d+0: 100.12
可以修改Context来改变Decimal运算的行为,例如精度、如何舍弃位数等等。例如下面的程序测试各种rounding设置对结果的影响:
# -*- coding: cp936 -*-
from decimal import *
con = getcontext()
con.prec = 5
print '-----------------------context------------------------'
print con
s = '100.005'
strs = [
'100.005',
'100.004',
'-100.005',
'-100.004',
]
round_methods = [
ROUND_CEILING,
ROUND_DOWN,
ROUND_FLOOR,
ROUND_HALF_DOWN,
ROUND_HALF_EVEN,
ROUND_HALF_UP,
ROUND_UP,
ROUND_05UP,
]
for method in round_methods:
con.rounding = method
print '----------------------------', con.rounding, '----------------------------'
for s in strs:
print ' %s+0:' % s, Decimal(s)+0
结果:
>>>
-----------------------context------------------------
Context(prec=5, rounding=ROUND_HALF_EVEN, Emin=-999999999, Emax=999999999, capitals=1, flags=[], traps=[DivisionByZero, Overflow, InvalidOperation])
---------------------------- ROUND_CEILING ----------------------------
100.005+0: 100.01
100.004+0: 100.01
-100.005+0: -100.00
-100.004+0: -100.00
---------------------------- ROUND_DOWN ----------------------------
100.005+0: 100.00
100.004+0: 100.00
-100.005+0: -100.00
-100.004+0: -100.00
---------------------------- ROUND_FLOOR ----------------------------
100.005+0: 100.00
100.004+0: 100.00
-100.005+0: -100.01
-100.004+0: -100.01
---------------------------- ROUND_HALF_DOWN ----------------------------
100.005+0: 100.00
100.004+0: 100.00
-100.005+0: -100.00
-100.004+0: -100.00
---------------------------- ROUND_HALF_EVEN ----------------------------
100.005+0: 100.00
100.004+0: 100.00
-100.005+0: -100.00
-100.004+0: -100.00
---------------------------- ROUND_HALF_UP ----------------------------
100.005+0: 100.01
100.004+0: 100.00
-100.005+0: -100.01
-100.004+0: -100.00
---------------------------- ROUND_UP ----------------------------
100.005+0: 100.01
100.004+0: 100.01
-100.005+0: -100.01
-100.004+0: -100.01
---------------------------- ROUND_05UP ----------------------------
100.005+0: 100.01
100.004+0: 100.01
-100.005+0: -100.01
-100.004+0: -100.01
>>>
文档里有一个将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='')
'<0.02>'
"""
q = Decimal(10) ** -places# 2 places --> '0.01'
sign, digits, exp = value.quantize(q).as_tuple()
result = []
digits = map(str, digits)
build, next = result.append, digits.pop
if sign:
build(trailneg)
for i in range(places):
build(next() if digits else '0')
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))