如何解決 Python Decimal.quantize() 發生 InvalidOperation

Decimal 是 Python 中好用的處理精確浮點數模組,但在某些情況下會發生 InvalidOperation,這邊分享遇到的原因以及解決方式。

問題如何發生?

在 python 下,嘗試建立一個很大的 Decimal 數值 5 * 10^31,然後對他做 quantize (也就是 decimal 的 round)到小數第二位,就會發生 Invalid Operation

>>> from decimal import Decimal
>>> d = Decimal('5.00E+31')
>>> d.quantize(Decimal('0.01'))

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
decimal.InvalidOperation: [<class 'decimal.InvalidOperation'>] 

為何發生 InvalidOperation?

主因是系統精度不夠造成,可以用以下指令確認目前最大精度

>>> from decimal import getcontext
>>> getcontext()

Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[InvalidOperation], traps=[InvalidOperation, DivisionByZero, Overflow])

目前系統精度是 28,若要讓輸入的 32 位數值能夠 round 到小數第 2 位,總共需要 32 + 2 = 34 位的精度,超過系統上限所以 raise InvalidOperation

quantize 時給定足夠的精度

在 quantize 時動態給定夠大的精度即可解決,詳細如下

>>> from decimal import Context
>>> d.quantize(Decimal('0.01'), context=Context(prec=34))
Decimal('50000000000000000000000000000000.00')

如果覺得我文章內容對你有幫助的話,請在文章後面幫我按 5 個讚!讓我知道大家都喜歡什麼內容哦!

範例原始碼在此下載:github

延伸閱讀:
Python 詭譎的 default parameter value ,由踩坑來學習!
被新創公司裁員後,我學到的五件事


參考資料:
Decimal.quantize raises InvalidOperation
Decimal fixed point and floating point arithmetic

Written by J
雖然大學唸的是生物,但持著興趣與熱情自學,畢業後轉戰硬體工程師,與宅宅工程師們一起過著沒日沒夜的生活,做著台灣最薄的 intel 筆電,要與 macbook air 比拼。 離開後,憑著一股傻勁與朋友創業,再度轉戰軟體工程師,一手扛起前後端、雙平台 app 開發,過程中雖跌跌撞撞,卻也累計不少經驗。 可惜不是那 1% 的成功人士,於是加入其他成功人士的新創公司,專職開發後端。沒想到卻在採前人坑的過程中,拓寬了眼界,得到了深層的領悟。