decimal.Decimal(n) % 1 在 n >= 100 时返回 InvalidOperation,DivisionImpossible。

7
我在 Django 应用程序中使用 Decimal 对象,发现了这个奇怪的错误:
ipdb> decimal.Decimal(10) % 1
    Decimal('0')
ipdb> decimal.Decimal(100) % 1
    *** decimal.InvalidOperation: [<class 'decimal.DivisionImpossible'>]
ipdb> decimal.Decimal(150) % 1
    *** decimal.InvalidOperation: [<class 'decimal.DivisionImpossible'>]
ipdb> decimal.Decimal(79) % 1
    Decimal('0')
ipdb> decimal.Decimal(100.1) % 2
    Decimal('0.10')
ipdb> decimal.Decimal(1000) % 2
    *** decimal.InvalidOperation: [<class 'decimal.DivisionImpossible'>]

更加神秘的是,直到数字变得非常大,这种情况在ipython中才会发生:

In [23]: decimal.Decimal(10**27) % 1
Out[23]: Decimal('0')

In [24]: decimal.Decimal(10**28) % 1
---------------------------------------------------------------------------
InvalidOperation                          Traceback (most recent call last)
<ipython-input-24-6ceaef82d283> in <module>()
----> 1 decimal.Decimal(10**28) % 1

InvalidOperation: [<class 'decimal.DivisionImpossible'>]

请注意,这个错误不仅限于ipdb:我发现这是因为Decimal(380) % 1破坏了我的django应用程序。
描述此错误的文档如下所述:
除法不可能 如果整数除法或余数运算的整数结果具有太多位数(将比精度更长),则会发生并发出无效操作信号。结果为[0,qNaN]。
有什么想法吗?

我无法在Python 2中复现。 - dawg
我可以在Python3.5中重现他最后的错误。Decimal(10**28) % 1 抛出了一个 decimal.InvalidOperation: [<class 'decimal.DivisionImpossible'>] - Adam Smith
3
检查/调整decimal.getcontext() - Stefan Pochmann
@AdamSmith 同样的问题,但实际上这符合文档中的错误信息。 - pretzlstyle
1
你可以通过以下方式解决这个问题: try: decimalobj % 1 except decimal.InvalidOperation: int(decimalobj) % 1,因为这只会出现在整数上面。 - Adam Smith
显示剩余3条评论
1个回答

4
我想我找到了答案。
看了源代码,我发现了这个:
# catch most cases of large or small quotient
expdiff = self.adjusted() - other.adjusted()
if expdiff >= context.prec + 1:
    # expdiff >= prec+1 => abs(self/other) > 10**prec
    return context._raise_error(DivisionImpossible)
if expdiff <= -2:
    # expdiff <= -2 => abs(self/other) < 0.1
    ans = self._rescale(ideal_exponent, context.rounding)
    return ans._fix(context)

在我的Django应用程序中,有一个关于“prec”的调整:

decimal.getcontext().prec = 2

我认为这还是有点错误,因为:

In [39]: decimal.getcontext().prec + 1
Out[39]: 3

In [40]: decimal.Decimal(100).adjusted() - decimal.Decimal(0).adjusted()
Out[40]: 2

因此,看起来100仍在其检查范围内(即2 < 3),但我非常有信心这是问题的根源。如果有人能为我解释这个库为什么这样做,我会很乐意更好地理解它。


我能够复现。 - dawg
1
你看错了源代码。这个URL看起来是3.2版本的源代码。3.5版本的源代码将所有内容委托给_decimal C模块,它是libmpdec的一个包装器。 - user2357112

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接