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)
<class 'decimal.Decimal'> 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
>>> 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章