比较python中的小数

时间:2013-06-26 07:28:23

标签: python testing decimal

我希望能够在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的测试?

4 个答案:

答案 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