Skip to content

Commit e0668af

Browse files
committed
[GR-10722] Not all functions from Math module are implemented correctly.
1 parent 6d9d321 commit e0668af

File tree

5 files changed

+382
-53
lines changed

5 files changed

+382
-53
lines changed

graalpython/com.oracle.graal.python.test/src/tests/test_math.py

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,12 @@ class MyFloat:
159159
def __float__(self):
160160
return 0.6
161161

162+
class MyInt(object):
163+
def __init__(self, value):
164+
self.value = value
165+
def __int__(self):
166+
return self.value
167+
162168
class MathTests(unittest.TestCase):
163169

164170
def ftest(self, name, got, expected, ulp_tol=5, abs_tol=0.0):
@@ -482,6 +488,11 @@ def test_basic_copysign(self):
482488
self.assertEqual(math.copysign(999999999999999999999.1, 1), 999999999999999999999.1)
483489
self.assertRaises(TypeError, math.copysign, 'hello', 1)
484490
self.assertRaises(TypeError, math.copysign, 1, 'hello')
491+
492+
self.assertEqual(math.copysign(MyFloat(), 1), 0.6)
493+
self.assertEqual(math.copysign(MyFloat(), -1), -0.6)
494+
self.assertEqual(math.copysign(1.2, MyFloat()), 1.2)
495+
self.assertEqual(math.copysign(MyFloat(), MyFloat()), 0.6)
485496

486497
def test_inf_copysign(self):
487498
self.assertEqual(math.copysign(1.0, float('inf')), 1.0)
@@ -928,6 +939,8 @@ def test_fabs(self):
928939
self.assertEqual(math.fabs(True), 1.0)
929940
self.assertEqual(math.fabs(False), 0.0)
930941

942+
self.assertEqual(math.fabs(MyFloat()), 0.6)
943+
931944
def test_factorial(self):
932945
self.assertRaises(ValueError, math.factorial, float('nan'))
933946
self.assertRaises(ValueError, math.factorial, float('inf'))
@@ -953,6 +966,12 @@ def test_factorial(self):
953966
self.assertEqual(math.factorial(13), 6227020800)
954967
self.assertEqual(math.factorial(30), 265252859812191058636308480000000)
955968
self.assertRaises(ValueError, math.factorial, -11.1)
969+
970+
self.assertEqual(math.factorial(MyInt(4)), 24)
971+
self.assertEqual(math.factorial(MyInt(True)), 1)
972+
self.assertRaises(TypeError, math.factorial, MyIndexable(4))
973+
self.assertRaises(TypeError, math.factorial, MyFloat())
974+
self.assertRaises(TypeError, math.factorial, MyInt(0.6))
956975

957976
def testGcd(self):
958977
if (sys.version_info.major >= 3 and sys.version_info.minor >= 5):
@@ -1038,6 +1057,15 @@ class TestNoFloor:
10381057
self.assertEqual(math.floor(2432902008176640000999), 2432902008176640000999)
10391058
self.assertEqual(math.floor(2432902008176640000999.99), 2432902008176640000999.99)
10401059

1060+
self.assertEqual(math.floor(MyFloat()), 0)
1061+
1062+
class MyFloorFloat():
1063+
def __floor__(self):
1064+
return 12
1065+
def __float(self):
1066+
return 112
1067+
self.assertEqual(math.floor(MyFloorFloat()), 12)
1068+
10411069
def test_fmod(self):
10421070
self.assertRaises(TypeError, math.fmod)
10431071
self.ftest('fmod(10, 1)', math.fmod(10, 1), 0.0)
@@ -1103,6 +1131,9 @@ def test_fmod(self):
11031131
self.assertEqual(math.fmod(2432902008176640000999, 12.12), 10.396369527944033)
11041132
self.assertEqual(math.fmod(-1e-100, 1e100), -1e-100)
11051133

1134+
self.assertEqual(math.fmod(MyFloat(), 1), 0.6)
1135+
self.assertEqual(math.fmod(MyFloat(), MyFloat()), 0.)
1136+
11061137
def testExp(self):
11071138
self.assertRaises(TypeError, math.exp)
11081139
self.ftest('exp(-1)', math.exp(-1), 1/math.e)
@@ -1174,6 +1205,7 @@ def getY():
11741205
testfrexp('frexp(Y(11.11))', math.frexp(Y(11.11)), (0.694375, 4))
11751206
testfrexp('frexp(2**1023)', math.frexp(2**1023), (0.5, 1024))
11761207
self.assertRaises(OverflowError, math.frexp, 2**1024)
1208+
testfrexp('frexp(MyFloat())', math.frexp(MyFloat()), (0.6, 0))
11771209

11781210
def test_ldexp(self):
11791211
self.assertRaises(TypeError, math.ldexp)
@@ -1223,6 +1255,13 @@ class II(int):
12231255
self.assertRaises(TypeError, math.ldexp, 1, 'Hello')
12241256
self.assertEqual(math.ldexp(7589167167882033, -48), 26.962138008038156)
12251257

1258+
self.assertRaises(TypeError, math.ldexp, 1, MyIndexable(2))
1259+
self.assertRaises(TypeError, math.ldexp, 1, MyInt(2))
1260+
self.assertRaises(TypeError, math.ldexp, 1, MyFloat())
1261+
self.assertEqual(math.ldexp(0.1, True), 0.2)
1262+
self.assertEqual(math.ldexp(MyFloat(),True), 1.2)
1263+
self.assertRaises(TypeError, math.ldexp, MyInt(2), MyFloat())
1264+
12261265
def test_trunc(self):
12271266
self.assertEqual(math.trunc(1), 1)
12281267
self.assertEqual(math.trunc(-1), -1)
@@ -1595,3 +1634,140 @@ def msum(iterable):
15951634

15961635
self.assertRaises(ValueError, math.fsum, [1., 2, INF, NINF])
15971636
self.assertEqual(math.fsum([1., 2, INF, INF]), INF)
1637+
1638+
class IsCloseTests(unittest.TestCase):
1639+
isclose = staticmethod(math.isclose) # sublcasses should override this
1640+
1641+
def assertIsClose(self, a, b, *args, **kwargs):
1642+
self.assertTrue(self.isclose(a, b, *args, **kwargs),
1643+
msg="%s and %s should be close!" % (a, b))
1644+
1645+
def assertIsNotClose(self, a, b, *args, **kwargs):
1646+
self.assertFalse(self.isclose(a, b, *args, **kwargs),
1647+
msg="%s and %s should not be close!" % (a, b))
1648+
1649+
def assertAllClose(self, examples, *args, **kwargs):
1650+
for a, b in examples:
1651+
self.assertIsClose(a, b, *args, **kwargs)
1652+
1653+
def assertAllNotClose(self, examples, *args, **kwargs):
1654+
for a, b in examples:
1655+
self.assertIsNotClose(a, b, *args, **kwargs)
1656+
1657+
def test_negative_tolerances(self):
1658+
# ValueError should be raised if either tolerance is less than zero
1659+
with self.assertRaises(ValueError):
1660+
self.assertIsClose(1, 1, rel_tol=-1e-100)
1661+
with self.assertRaises(ValueError):
1662+
self.assertIsClose(1, 1, rel_tol=1e-100, abs_tol=-1e10)
1663+
1664+
def test_identical(self):
1665+
# identical values must test as close
1666+
identical_examples = [(2.0, 2.0),
1667+
(0.1e200, 0.1e200),
1668+
(1.123e-300, 1.123e-300),
1669+
(12345, 12345.0),
1670+
(0.0, -0.0),
1671+
(345678, 345678)]
1672+
self.assertAllClose(identical_examples, rel_tol=0.0, abs_tol=0.0)
1673+
1674+
def test_eight_decimal_places(self):
1675+
# examples that are close to 1e-8, but not 1e-9
1676+
eight_decimal_places_examples = [(1e8, 1e8 + 1),
1677+
(-1e-8, -1.000000009e-8),
1678+
(1.12345678, 1.12345679)]
1679+
self.assertAllClose(eight_decimal_places_examples, rel_tol=1e-8)
1680+
self.assertAllNotClose(eight_decimal_places_examples, rel_tol=1e-9)
1681+
1682+
def test_near_zero(self):
1683+
# values close to zero
1684+
near_zero_examples = [(1e-9, 0.0),
1685+
(-1e-9, 0.0),
1686+
(-1e-150, 0.0)]
1687+
# these should not be close to any rel_tol
1688+
self.assertAllNotClose(near_zero_examples, rel_tol=0.9)
1689+
# these should be close to abs_tol=1e-8
1690+
self.assertAllClose(near_zero_examples, abs_tol=1e-8)
1691+
1692+
def test_identical_infinite(self):
1693+
# these are close regardless of tolerance -- i.e. they are equal
1694+
self.assertIsClose(INF, INF)
1695+
self.assertIsClose(INF, INF, abs_tol=0.0)
1696+
self.assertIsClose(NINF, NINF)
1697+
self.assertIsClose(NINF, NINF, abs_tol=0.0)
1698+
1699+
def test_inf_ninf_nan(self):
1700+
# these should never be close (following IEEE 754 rules for equality)
1701+
not_close_examples = [(NAN, NAN),
1702+
(NAN, 1e-100),
1703+
(1e-100, NAN),
1704+
(INF, NAN),
1705+
(NAN, INF),
1706+
(INF, NINF),
1707+
(INF, 1.0),
1708+
(1.0, INF),
1709+
(INF, 1e308),
1710+
(1e308, INF)]
1711+
# use largest reasonable tolerance
1712+
self.assertAllNotClose(not_close_examples, abs_tol=0.999999999999999)
1713+
1714+
def test_zero_tolerance(self):
1715+
# test with zero tolerance
1716+
zero_tolerance_close_examples = [(1.0, 1.0),
1717+
(-3.4, -3.4),
1718+
(-1e-300, -1e-300)]
1719+
self.assertAllClose(zero_tolerance_close_examples, rel_tol=0.0)
1720+
1721+
zero_tolerance_not_close_examples = [(1.0, 1.000000000000001),
1722+
(0.99999999999999, 1.0),
1723+
(1.0e200, .999999999999999e200)]
1724+
self.assertAllNotClose(zero_tolerance_not_close_examples, rel_tol=0.0)
1725+
1726+
def test_asymmetry(self):
1727+
# test the asymmetry example from PEP 485
1728+
self.assertAllClose([(9, 10), (10, 9)], rel_tol=0.1)
1729+
1730+
def test_integers(self):
1731+
# test with integer values
1732+
integer_examples = [(100000001, 100000000),
1733+
(123456789, 123456788)]
1734+
1735+
self.assertAllClose(integer_examples, rel_tol=1e-8)
1736+
self.assertAllNotClose(integer_examples, rel_tol=1e-9)
1737+
1738+
# TODO the test is commented out due to GR-10712
1739+
'''
1740+
def test_decimals(self):
1741+
# test with Decimal values
1742+
from decimal import Decimal#
1743+
1744+
decimal_examples = [(Decimal('1.00000001'), Decimal('1.0')),
1745+
(Decimal('1.00000001e-20'), Decimal('1.0e-20')),
1746+
(Decimal('1.00000001e-100'), Decimal('1.0e-100')),
1747+
(Decimal('1.00000001e20'), Decimal('1.0e20'))]
1748+
self.assertAllClose(decimal_examples, rel_tol=1e-8)
1749+
self.assertAllNotClose(decimal_examples, rel_tol=1e-9)
1750+
'''
1751+
1752+
# TODO the test is commented out due to GR-10711
1753+
'''
1754+
def test_fractions(self):
1755+
# test with Fraction values
1756+
from fractions import Fraction
1757+
1758+
fraction_examples = [
1759+
(Fraction(1, 100000000) + 1, Fraction(1)),
1760+
(Fraction(100000001), Fraction(100000000)),
1761+
(Fraction(10**8 + 1, 10**28), Fraction(1, 10**20))]
1762+
self.assertAllClose(fraction_examples, rel_tol=1e-8)
1763+
self.assertAllNotClose(fraction_examples, rel_tol=1e-9)
1764+
'''
1765+
def test_objects(self):
1766+
# these are close regardless of tolerance -- i.e. they are equal
1767+
self.assertIsClose(MyFloat(), MyFloat())
1768+
self.assertIsClose(MyFloat(), MyFloat(), abs_tol=0.0)
1769+
self.assertIsClose(MyFloat(), MyFloat(), abs_tol=MyFloat())
1770+
self.assertIsClose(MyFloat(), MyFloat(), rel_tol=0.0)
1771+
self.assertIsClose(MyFloat(), MyFloat(), rel_tol=MyFloat())
1772+
1773+
self.assertIsNotClose(MyFloat(), 10)

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinFunctions.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@
121121
import com.oracle.graal.python.nodes.object.GetClassNode;
122122
import com.oracle.graal.python.nodes.subscript.GetItemNode;
123123
import com.oracle.graal.python.nodes.truffle.PythonArithmeticTypes;
124-
import com.oracle.graal.python.nodes.util.CastToIntNode;
124+
import com.oracle.graal.python.nodes.util.CastToIntegerFromIndexNode;
125125
import com.oracle.graal.python.runtime.PythonContext;
126126
import com.oracle.graal.python.runtime.PythonCore;
127127
import com.oracle.graal.python.runtime.PythonOptions;
@@ -237,7 +237,7 @@ public String doPI(PInt x) {
237237

238238
@Specialization
239239
public String doO(Object x,
240-
@Cached("create()") CastToIntNode toIntNode,
240+
@Cached("create()") CastToIntegerFromIndexNode toIntNode,
241241
@Cached("create()") BinNode recursiveNode) {
242242
Object value = toIntNode.execute(x);
243243
return recursiveNode.executeObject(value);

0 commit comments

Comments
 (0)