mirror of
https://github.com/Gnucash/gnucash.git
synced 2026-11-03 12:50:50 -06:00
[bindings/python] Floor GncNumeric // and make int() exact
GncNumeric // truncated towards zero, so GncNumeric(-7, 2) // 1 gave -3 where Python's // floors to -4. int() converted through a float and lost precision above 2**53: int(GncNumeric(2**53 + 1)) returned 9007199254740992. Round // with GNC_HOW_RND_FLOOR and convert int() through the exact fraction. int() now goes through to_fraction(), which read a negative denominator as a divisor; GnuCash treats it as a multiplier, so GncNumeric(3, -100) is 300. to_fraction() now does the same.
This commit is contained in:
1 parent
65949b43f0
commit
faf7a9999d
2 files changed
+27
-2
No files matched your search
@@ -648,7 +648,7 @@ class GncNumeric(GnuCashCoreClass):
|
||||
return a.div(b, GNC_DENOM_AUTO, GNC_HOW_RND_ROUND)
|
||||
|
||||
def _floordiv(a, b):
|
||||
return a.div(b, 1, GNC_HOW_RND_TRUNC)
|
||||
return a.div(b, 1, GNC_HOW_RND_FLOOR)
|
||||
|
||||
__add__, __radd__ = _operator_fallbacks(_add, operator.add)
|
||||
__iadd__ = __add__
|
||||
@@ -722,7 +722,7 @@ class GncNumeric(GnuCashCoreClass):
|
||||
return self.to_double()
|
||||
|
||||
def __int__(self):
|
||||
return int(self.to_double())
|
||||
return int(self.to_fraction())
|
||||
|
||||
def __pos__(a):
|
||||
"""+a"""
|
||||
@@ -738,6 +738,9 @@ class GncNumeric(GnuCashCoreClass):
|
||||
|
||||
def to_fraction(self):
|
||||
from fractions import Fraction
|
||||
# A negative denominator multiplies: 3/-100 is 300.
|
||||
if self.denom() < 0:
|
||||
return Fraction(self.num() * -self.denom())
|
||||
return Fraction(self.num(), self.denom())
|
||||
|
||||
def __str__(self):
|
||||
|
||||
@@ -95,6 +95,28 @@ class TestGncNumeric(TestCase):
|
||||
self.assertEqual(10 + GncNumeric(3), GncNumeric(13))
|
||||
self.assertEqual(10 * GncNumeric(3), GncNumeric(30))
|
||||
|
||||
def test_to_int(self):
|
||||
# int() truncates towards zero and must not round through a float.
|
||||
self.assertEqual(int(GncNumeric(7, 2)), 3)
|
||||
self.assertEqual(int(GncNumeric(-7, 2)), -3)
|
||||
self.assertEqual(int(GncNumeric(2**53 + 1)), 2**53 + 1)
|
||||
self.assertEqual(int(GncNumeric(-(2**62 + 1), 1)), -(2**62 + 1))
|
||||
self.assertEqual(int(GncNumeric(3, -100)), 300)
|
||||
self.assertEqual(int(GncNumeric(-3, -100)), -300)
|
||||
|
||||
def test_floordiv(self):
|
||||
# // rounds towards negative infinity, as it does for int and Fraction.
|
||||
self.assertEqual(GncNumeric(7, 2) // 1, GncNumeric(3))
|
||||
self.assertEqual(GncNumeric(-7, 2) // 1, GncNumeric(-4))
|
||||
self.assertEqual(GncNumeric(-1, 2) // 1, GncNumeric(-1))
|
||||
self.assertEqual(GncNumeric(-7) // GncNumeric(2), GncNumeric(-4))
|
||||
self.assertEqual(-7 // GncNumeric(2), GncNumeric(-4))
|
||||
self.assertEqual(GncNumeric(-6) // 3, GncNumeric(-2))
|
||||
self.assertEqual(GncNumeric(7) // -2, GncNumeric(-4))
|
||||
self.assertEqual(GncNumeric(-7) // GncNumeric(-2), GncNumeric(3))
|
||||
self.assertEqual(GncNumeric(7) // GncNumeric(3, 2), GncNumeric(4))
|
||||
self.assertEqual(GncNumeric(-7) // GncNumeric(3, 2), GncNumeric(-5))
|
||||
|
||||
def test_incorect_args(self):
|
||||
with self.assertRaises(TypeError):
|
||||
GncNumeric(1, 2, 3)
|
||||
|
||||
Reference in new issue
Block a user