I am using Decimal objects in a django app, and found this strange error:
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'>]
Even more mysteriously, this doesn't happen in ipython until the numbers get very large:
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'>]
Note that the error is not confined to ipdb: I discovered this because Decimal(380) % 1 was breaking my django app.
The documentation describing this error says:
Division impossible
This occurs and signals invalid-operation if the integer result of a divide-integer or remainder operation had too many digits (would be longer than precision). The result is [0,qNaN].
Any ideas?
Decimal is based on the Decimal Arithmetic specification. You can see here that "Division impossible" means that the integer result of a divide-integer or remainder operation had too many digits (would be longer than precision). This precision is something you can adjust:
This error occurs because of the approximations in floating point numbers due to which sum of 1.2 and 2.2 is not equal to 3.4. So, In cases where decimal values need to be compared, we should use the decimal module instead of floating point numbers. This can be more clear from the following example.
/Decimal Module in Python Decimal Module in Python Author: Aditya Raj Last Updated: June 24, 2021 Python has numeric data typeslike int, float and complex numbers but due to the machine dependent nature of floating point numbers, we need a more precise data type for calculations which demand high precision.
Therefore, For such applications, we need a machine independent data type to implement decimal numbers which has been implemented using the decimal module in python. Also, decimal module has implementation of decimal number for a precision of upto 28 decimal digits while floating point numbers have only a precision of upto 18 digits.
I think I figured it out.
Looking at the source code, I found this:
# 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)
And in my django app, there's an adjustment to the prec:
decimal.getcontext().prec = 2
This still looks slightly wrong to me, because:
In [39]: decimal.getcontext().prec + 1
Out[39]: 3
In [40]: decimal.Decimal(100).adjusted() - decimal.Decimal(0).adjusted()
Out[40]: 2
And so it still looks like 100 is within the bounds of what it's checking for (that is, 2 < 3
), but I am pretty confident this is the source of the problem. If anyone can illuminate for me why the library does this, I would love to understand it better.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With