我希望能够在Python中比较Decimals。为了用钱计算,聪明的人告诉我用Decimals而不是浮点数,所以我做了。但是,如果我想验证计算是否产生预期结果,我该怎么做呢?
>>> a = Decimal(1./3.)
>>> a
Decimal('0.333333333333333314829616256247390992939472198486328125')
>>> b = Decimal(2./3.)
>>> b
Decimal('0.66666666666666662965923251249478198587894439697265625')
>>> a == b
False
>>> a == b - a
False
>>> a == b - Decimal(1./3.)
False
所以在这个例子中a = 1/3和b = 2/3,所以很明显b-a = 1/3 = a,然而,这不能用小数来完成。
我想这样做的方法是说我希望结果是1/3,而在python中我把它写成
Decimal(1./3.).quantize(...)
然后我可以这样比较:
(b-a).quantize(...) == Decimal(1./3.).quantize(...)
所以,我的问题是:有更清洁的方法吗?你会如何编写Decimals的测试?
答案 0 :(得分:18)
您没有以正确的方式使用Decimal
。
>>> from decimal import *
>>> Decimal(1./3.) # Your code
Decimal('0.333333333333333314829616256247390992939472198486328125')
>>> Decimal("1")/Decimal("3") # My code
Decimal('0.3333333333333333333333333333')
在“您的代码”中,您实际执行“经典”浮点除法 - 然后将结果转换为小数。 浮动引入的错误会传播到 Decimal 。
在“我的代码”中,我执行 Decimal 分区。生成一个正确的(但截断的)结果直到最后一位数。
关于四舍五入。如果您使用货币数据,则必须知道用于业务舍入的规则。如果不是这样,使用Decimal
将不自动解决您的所有问题。这是一个例子:3个股东之间可以分享100美元。
>>> TWOPLACES = Decimal(10) ** -2
>>> dividende = Decimal("100.00")
>>> john = (dividende / Decimal("3")).quantize(TWOPLACES)
>>> john
Decimal('33.33')
>>> paul = (dividende / Decimal("3")).quantize(TWOPLACES)
>>> georges = (dividende / Decimal("3")).quantize(TWOPLACES)
>>> john+paul+georges
Decimal('99.99')
Oups:错过$ .01(银行的免费礼物?)
答案 1 :(得分:2)
您的措辞表明您想要进行货币计算,注意您的回合错误。小数是一个不错的选择,因为它们在加法,减法和与其他小数相乘时产生精确的结果。
奇怪的是,您的示例显示使用“1/3”分数。我从来没有在我的银行中准确存入“三分之一美元”......这是不可能的,因为没有这样的货币单位!
我的观点是,如果你正在做任何部门,那么你需要了解你正在做什么,组织的政策是什么......在这种情况下应该可以实现你想要的用十进制量化。
现在 - 如果你真的希望对小数进行分割,并且你想要随意携带“精确”,你真的不想使用Decimal
对象... 您想使用Fraction
对象。
有了这个,你的例子就会这样:
>>> from fractions import Fraction
>>> a = Fraction(1,3)
>>> a
Fraction(1, 3)
>>> b = Fraction(2,3)
>>> b
Fraction(2, 3)
>>> a == b
False
>>> a == b - a
True
>>> a + b == Fraction(1, 1)
True
>>> 2 * a == b
True
好吧,好吧,甚至在那里发出警告:Fraction
个对象是两个整数的比率,所以你需要乘以10的正确幂并将其带到ad-hoc附近。
声音太多了吗?是的......它可能是!
所以,回到Decimal对象;在十进制除法和十进制乘法上实现量化/舍入。
答案 2 :(得分:1)
浮点算术不准确:
十进制数字可以准确表示。相比之下,数字就像 1.1和2.2在二进制浮点中没有精确的表示。最终用户通常不希望
1.1 + 2.2
显示为3.3000000000000003
和二进制浮点一样
你必须选择一个分辨率并截断它之后的所有内容:
>>> from decimal import *
>>> getcontext().prec = 6
>>> Decimal(1) / Decimal(7)
Decimal('0.142857')
>>> getcontext().prec = 28
>>> Decimal(1) / Decimal(7)
Decimal('0.1428571428571428571428571429')
你显然会得到一些舍入错误,这会随着操作次数的增加而增加,所以你必须仔细选择你的分辨率。
答案 3 :(得分:0)
还有另一种可能对您有用的方法:
round(val, places)
例如:
>>> a = 1./3
>>> a
0.33333333333333331
>>> b = 2./3
>>> b
0.66666666666666663
>>> b-a
0.33333333333333331
>>> round(a,2) == round(b-a, 2)
True
如果您愿意,可以创建一个函数equals_to_the_cent()
:
>>> def equals_to_the_cent(a, b):
... return round(a, 2) == round(b, 2)
...
>>> equals_to_the_cent(a, b)
False
>>> equals_to_the_cent(a, b-a)
True
>>> equals_to_the_cent(1-a, b)
True