python的decimal

        Python自带的decimal模块用于十进制数学计算,它是在浮点类型的基础上设计的,可以非常精确地在计算机中存储和计算,精度优于floating point,因为浮点数并不能精确的表示十进制数,因为计算机由底层CPU和IEEE 754标准通过自己的浮点单位去执行算术时的特征,因此对于精度要求高但效率不要求的场景,比如财务等,decimal可以较好的替换float类型。

        Decimal重载了简单的算术运算符,所以可以采用内置数值类型同样的方式处理 Decimal实例。Decimal构造函数取一个整数或字符串作为参数。使用浮点数创建 Decimal 之前,可以先将浮点数转换为一个字符串,使调用者能够显式地处理值得位数,还可以由元组创建,其中包含一个符号标志(0 表示正,1 表示负)、数字 tuple 以及一个整数指数。 

>>> 
>>> f1 = 1.23
>>> f2 = 3.21
>>> f1 + f2
4.4399999999999995
>>> 
>>> from decimal import Decimal
>>> from decimal import getcontext
>>> 
>>> d1 = Decimal('1.23')
>>> d2 = Decimal('3.21')
>>> d3 = d1 + d2
>>> print(type(d3), d3)
 4.44
>>> # 很准,位数也不变
>>> 
>>> 
>>> t1 = (1, (1, 1), -2)
>>> Decimal(t1)
Decimal('-0.11')
>>> t2 = (1, (1, 2), 3)
>>> Decimal(t2)
Decimal('-1.2E+4')
>>> t3 = (0, (1, 2), 3)
>>> Decimal(t3)
Decimal('1.2E+4')
>>> 
>>> str(f3)
'2.468'
>>> 

但相除或相乘的话,小数点的位数还是变了:

>>> 
>>> d4 = d2 / d1
>>> d4
Decimal('2.609756097560975609756097561')
>>> d3
Decimal('4.44')
>>> d5 = d1 * d2
>>> d5
Decimal('3.9483')
>>> 

可以通过getcontext().prec = x(x为你想要的精度来设置)来设置Decimal类型保留有效位数,注意不是保留小数的位数,如果要保留小数位数用round()。

>>> 
>>> context = getcontext()
>>> context.prec = 3  # 保留3位数
>>> print(context)
Context(prec=3, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[Inexact, Rounded], traps=[InvalidOperation, DivisionByZero, Overflow])
>>> d6 = d1 * d2
>>> d6
Decimal('3.95')
>>> d7 = d2 / d1
>>> d7
Decimal('2.61')
>>> d8 = d1 + d2
>>> d8
Decimal('4.44')
>>> 

由上可以看到,设置一次getcontext().prec = x后,其下面的Decimal类型全部保留了x位数,有时不需要它管辖得太广,那么我们需要用localcontext创建一个局部上下文。

>>> from decimal import localcontext
>>> 
>>> with localcontext() as ctx:
	ctx.prec = 4
	d9 = d2 / d1
	d10 = d1 + d2
	print(d9, d10)

	
2.610 4.44
>>> with localcontext() as ctx:
	ctx.prec = 5
	d11 = d2 / d1
	d12 = d1 * d2
	print(d11, d12)

	
2.6098 3.9483
>>> 

 

>>> from decimal import Decimal
>>> 
>>> v = Decimal(str(1.536842))
>>> v1 = Decimal(str(3.0125873))
>>> v
Decimal('1.536842')
>>> v1
Decimal('3.0125873')
>>> 
>>> from decimal import localcontext
>>> with localcontext() as ctx:
	ctx.prec = 5  # 保留5位有效数字
	v2 = v + v1
	v3 = v * v1
	print(v2, v3)

	
4.5494 4.6299
>>> 
>>> # 保留5位小数
>>> v4 = round(float(v + v1), 5)
>>> v4
4.54943
>>> v5 = round(float(v * v1), 5)
>>> v5
4.62987
>>> 

如果觉得局部上下文设置精度太不灵活,要用的时候都得设置一次,那么还可以使用实例上下文。

>>> 
>>> ctx1 = getcontext().copy()  # 创建一个带精度的context
>>> ctx1.prec = 2  # 设置context精度
>>> ctx2 = getcontext().copy()  # 创建一个带精度的context
>>> ctx2.prec = 5  # 设置context精度
>>> 
>>> d13 = ctx1.create_decimal(str(f1))
>>> d13
Decimal('1.2')
>>> d14 = ctx1.create_decimal(str(f2))
>>> d14
Decimal('3.2')
>>> d13 + d14
Decimal('4.4')
>>> d13 * d14
Decimal('3.84')
>>> d14 / d13
Decimal('2.67')
>>> # 从上面看到,create_decimal创建Decimal实例时精度是对的,但是他们的实例进行算术运算后,精度就变了
>>> d15 = ctx2.create_decimal(str(f1))
>>> d15
Decimal('1.23')
>>> d16 = ctx2.create_decimal(str(f2))
>>> d16
Decimal('3.21')
>>> d15 + d16
Decimal('4.44')
>>> d15 * d16
Decimal('3.95')
>>> d16 / d15
Decimal('2.61')
>>> 
>>> f3 = 2.468
>>> ctx3 = getcontext().copy()  # 创建一个带精度的context
>>> ctx3.prec = 5  # 设置context精度
>>> 
>>> d17 = ctx3.create_decimal(str(f3))
>>> d17
Decimal('2.468')
>>> d17 + d15
Decimal('3.70')
>>> d17 * d15
Decimal('3.04')
>>> d17 / d15
Decimal('2.01')
>>> 

rounding取整,有多种选择,以保证值在所需精度范围内。
•ROUND_CEILING 总是趋向于无穷大向上取整。
•ROUND_DOWN 总是趋向 0 取整。
•ROUND_FLOOR 总是趋向负无穷大向下取整。
•ROUND_HALF_DOWN 如果最后一个有效数字大于或等于 5 则朝 0 反方向取整;否则,趋向 0 取整。
•ROUND_HALF_EVEN 类似于 ROUND_HALF_DOWN,不过,如果最后一个有效数字值为 5,则会检查前一位。偶数值会导致结果向下取整,奇数值导致结果向上取整。
•ROUND_HALF_UP 类似于 ROUND_HALF_DOWN,不过如果最后一位有效数字为 5,值会朝 0 的反方向取整。
•ROUND_UP 朝 0 的反方向取整。
•ROUND_05UP 如果最后一位是 0 或 5,则朝 0 的反方向取整;否则向 0 取整。

>>> 
>>> import decimal
>>> 
>>> context = decimal.getcontext()
>>> ROUNDING_MODES = [
  'ROUND_CEILING',
  'ROUND_DOWN',
  'ROUND_FLOOR',
  'ROUND_HALF_DOWN',
  'ROUND_HALF_EVEN',
  'ROUND_HALF_UP',
  'ROUND_UP',
  'ROUND_05UP',
  ]
>>> context.prec = 3
>>> for mode in ROUNDING_MODES:
	context.rounding = getattr(decimal, mode)
	value = decimal.Decimal(str(f3))
	print(value)

	
2.468
2.468
2.468
2.468
2.468
2.468
2.468
2.468
>>> 
>>> context.prec = 3
>>> for mode in ROUNDING_MODES:
	context.rounding = getattr(decimal, mode)
	value = decimal.Decimal(str(f3)) / decimal.Decimal('1.25689')
	print(value)

	
1.97
1.96
1.96
1.96
1.96
1.96
1.97
1.96

除了期望的数字值,Decimal 还可以表示很多特殊值,包括正负无穷大值、“不是一个数”(NaN)和 0,python并没有特殊的语法来表示这些特殊的浮点值,但是可以使用float()来创建它们。

>>> import decimal
>>> 
>>> for value in ['Infinity', 'inf', 'NaN', 'nan', '0']:
	print (decimal.Decimal(value), decimal.Decimal('-' + value))

	
Infinity -Infinity
Infinity -Infinity
NaN -NaN
NaN -NaN
0 -0

与无穷大值相加会返回另一个无穷大值。与 NaN 比较相等性总会返回 false,而比较不等性总会返回 true。与 NaN 比较大小来确定排序顺序没有明确定义,这会导致一个错误。

>>> 
>>> decimal.Decimal('inf') + 1
Decimal('Infinity')
>>> decimal.Decimal('-inf') + 100
Decimal('-Infinity')
>>> decimal.Decimal('nan') == decimal.Decimal('inf')
False
>>> decimal.Decimal('nan') == decimal.Decimal('-inf')
False
>>> decimal.Decimal('nan') != decimal.Decimal(0)
True
>>> 

 

你可能感兴趣的:(Python)