项目中有小数计算,需要限制小数点后两位,float类型计算有误差,于是使用decimal类型计算
用法
1、可以通过限制decimal环境条件。
from decimal import Decimal, getcontext, setcontext
getcontext().prec = 2
# 也可以直接设置decimal环境变量
mycontext = Context(prec=2, rounding=ROUND_HALF_DOWN)
setcontext(mycontext)
- prec为精度值,希望限制到小数点后几位
- ROUND_HALF_DOWN为round类型(四舍五入类型)
- 官方文档全部round类型解释
1、 ROUND_CEILING (towards Infinity),如果Decimal为正,则做ROUND_UP操作;如果Decimal为负,则做ROUND_DOWN操作;
2、 ROUND_DOWN (towards zero),舍弃小数部分,从不在前面数字做增加操作,如5.21->5.2;
3、 ROUND_FLOOR (towards -Infinity),如果Decimal为负,则做ROUND_UP操作;如果Decimal为正,则做ROUND_DOWN操作;
4、 ROUND_HALF_DOWN (to nearest with ties going towards zero),如果舍弃部分>.5,则做ROUND_UP操作;否则,做ROUND_DOWN操作;
5、 ROUND_HALF_EVEN (to nearest with ties going to nearest even integer),如果舍弃部分左边的数字是奇数,则做ROUND_HALF_UP操作;若为偶数,则做ROUND_HALF_DOWN操作;
6、 ROUND_HALF_UP ,如果舍弃部分>=.5,则做ROUND_UP操作;否则,做ROUND_DOWN操作;
7、 ROUND_UP (away from zero),舍弃小数部分非0时,在前面增加数字,如 5.21 -> 5.3;
8、 ROUND_05UP (away from zero if last digit after rounding towards zero would have been 0 or 5; otherwise towards zero)
- 官方文档全部round类型解释
>>> Decimal(1)/Decimal(3)
Decimal('0.33')
2、如果希望只限制结果(单个数字)
from decimal import Decimal, ROUND_HALF_UP
num = Decimal(12314.424234).quantize(Decimal('0.00'), ROUND_HALF_UP)
>>> Decimal(12314.424234).quantize(Decimal('0.00'))
Decimal('12314.42')
遇到的问题
本地环境下项目运转正常,部署到测试服务器上报错,查看错误信息,发现是Decimal相关的问题。
decimal.InvalidOperation: [<class 'decimal.InvalidOperation'>]
很奇怪的是,相同代码环境不同结果报错。在网上找了很久没有找到解释。但是从网上一个博客中一段话启发了我。
参数max_digits的值必须大于decimal_places的值。
0.10000 有效位是5位,但是1.00000的有效位是6位。
因此,如果max_digits=5, decimal_places=5, 那么,任何大于或者等于1的值出现都将报错。
虽然这讲的是DecimalFields字段的“decimal_places”() 和 “max_digits” 属性,但是我觉得原理相同,所以我怀疑是decimal的长度太小。
于是我使用decimal计算时,计算值和结果都小于1时没有报错。
之后我用getcontext()获取服务器上Decimal环境,发现确实被限制。
最后从代码中找到问题:
getcontext().prec = 2
Decimal(12314.424234).quantize(Decimal('0.00'))
这两个无法同时使用,我删除getcontext().prec = 2
之后,代码就没有问题了,原因我猜可能是getcontext().prec = 2
没有影响到本地环境吧。