5

我希望能够比较 Python 中的小数。为了用钱计算,聪明的人告诉我用小数而不是浮点数,所以我做了。但是,如果我想验证计算是否产生了预期的结果,我该怎么做呢?

>>> 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,所以显然 ba = 1/3 = a,但是,这不能用小数来完成。

我想一种方法是说我希望结果是 1/3,在 python 中我把它写成

Decimal(1./3.).quantize(...)

然后我可以像这样比较它:

(b-a).quantize(...) == Decimal(1./3.).quantize(...)

所以,我的问题是:有没有更清洁的方法呢?您将如何为小数编写测试?

4

4 回答 4

25

你没有使用Decimal正确的方法。

>>> from decimal import *

>>> Decimal(1./3.)                  # Your code
Decimal('0.333333333333333314829616256247390992939472198486328125')

>>> Decimal("1")/Decimal("3")       # My code
Decimal('0.3333333333333333333333333333')

在“您的代码”中,您实际上执行“经典”浮点除法——然后将结果转换为小数。浮点数引入的错误会传播到您的Decimal

在“我的代码”中,我进行小数除法。产生一个正确(但被截断)的结果,直到最后一位。


关于四舍五入。如果您使用货币数据,您必须了解在您的业务中用于四舍五入的规则。如果不是这样,使用不会Decimal自动解决您的所有问题。这是一个例子:100 美元由 3 个股东分享。

>>> 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:缺少 0.01 美元(银行的免费礼物?)

于 2013-06-26T07:38:02.197 回答
2

你的措辞表明你想要进行货币计算,注意你的四舍五入错误。小数是一个不错的选择,因为它们在与其他小数的加法、减法和乘法下会产生精确的结果。

奇怪的是,您的示例显示使用分数“1/3”。我从来没有将“三分之一美元”存入我的银行……这是不可能的,因为没有这样的货币单位!

我的观点是,如果你正在做任何 DIVISION,那么你需要了解你正在尝试做什么,组织对这类事情的政策是什么......在这种情况下,应该可以使用 Decimal 量化来实现你想要的.

现在——如果你真的想做小数除法,并且你想携带任意的“精确性”,你真的不想使用这个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 的右幂并随身携带。

听起来工作量太大?是的......它可能是!

所以,回到 Decimal 对象;在十进制除法和十进制乘法上实现量化/舍入。

于 2014-04-01T02:19:06.100 回答
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')

您显然会得到一些舍入误差,该误差会随着操作次数的增加而增加,因此您必须仔细选择分辨率。

于 2013-06-26T07:39:39.460 回答
-1

还有另一种方法可能对您有用:

  • 继续以浮点值进行所有计算
  • 当您需要比较是否相等时,请使用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
于 2014-04-02T12:27:39.897 回答