Skip to content

Commit ab06141

Browse files
committed
[GR-10284] Math.trunc function is not implemented.
PullRequest: graalpython/75
2 parents 026f4a5 + 5abeed8 commit ab06141

File tree

8 files changed

+188
-0
lines changed

8 files changed

+188
-0
lines changed

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,3 +646,20 @@ def subclassTest(number):
646646
subclassTest(9.1)
647647
subclassTest(6227020800.2)
648648
subclassTest(9999992432902008176640000999999.33)
649+
650+
def test_trunc(self):
651+
self.assertEqual(float(1).__trunc__(), 1)
652+
self.assertEqual(float(1.99).__trunc__(), 1)
653+
self.assertEqual(float(-1.99).__trunc__(), -1)
654+
655+
self.assertRaises(ValueError, float('nan').__trunc__)
656+
self.assertRaises(OverflowError, float('inf').__trunc__)
657+
self.assertRaises(OverflowError, float('-inf').__trunc__)
658+
659+
self.assertEqual(MyFloat(1).__trunc__(), 1)
660+
self.assertEqual(MyFloat(1.99).__trunc__(), 1)
661+
self.assertEqual(MyFloat(-1.99).__trunc__(), -1)
662+
663+
self.assertRaises(ValueError, MyFloat('nan').__trunc__)
664+
self.assertRaises(OverflowError, MyFloat('inf').__trunc__)
665+
self.assertRaises(OverflowError, MyFloat('-inf').__trunc__)

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

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,3 +249,64 @@ def subclassTest(number):
249249
subclassTest(9)
250250
subclassTest(6227020800)
251251
subclassTest(9999992432902008176640000999999)
252+
253+
class MyTrunc:
254+
def __trunc__(self):
255+
return 1972
256+
class MyIntTrunc:
257+
def __trunc__(self):
258+
return 1972
259+
def __int__(self):
260+
return 66
261+
262+
def test_trunc():
263+
def builtinTest(number):
264+
a = int(number)
265+
b = a.__trunc__()
266+
assert a == b
267+
assert a is b
268+
assert type(a) == int
269+
assert type(b) == int
270+
271+
builtinTest(-9)
272+
builtinTest(0)
273+
builtinTest(9)
274+
builtinTest(6227020800)
275+
builtinTest(9999992432902008176640000999999)
276+
277+
assert True.__trunc__() == 1
278+
assert False.__trunc__() == 0
279+
280+
assert int(MyTrunc()) == 1972
281+
assert int(MyIntTrunc()) == 66
282+
283+
def test_trunc_subclass():
284+
def subclassTest(number):
285+
a = MyInt(number)
286+
b = a.__trunc__()
287+
assert a == b
288+
assert a is not b
289+
assert type(a) == MyInt
290+
assert type(b) == int
291+
292+
subclassTest(-9)
293+
subclassTest(0)
294+
subclassTest(9)
295+
subclassTest(6227020800)
296+
subclassTest(9999992432902008176640000999999)
297+
298+
assert MyInt(MyTrunc()) == 1972
299+
assert MyInt(MyIntTrunc()) == 66
300+
301+
def test_create_int_from_bool():
302+
303+
class SpecInt1:
304+
def __int__(self):
305+
return True
306+
307+
class SpecInt0:
308+
def __int__(self):
309+
return False
310+
311+
assert int(SpecInt1()) == 1
312+
assert int(SpecInt0()) == 0

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,14 @@ def ftest(self, name, got, expected, ulp_tol=5, abs_tol=0.0):
102102
if failure is not None:
103103
self.fail("{}: {}".format(name, failure))
104104

105+
def testConstants(self):
106+
# Ref: Abramowitz & Stegun (Dover, 1965)
107+
self.ftest('pi', math.pi, 3.141592653589793238462643)
108+
self.ftest('e', math.e, 2.718281828459045235360287)
109+
if (sys.version_info.major >= 3 and sys.version_info.minor >= 6):
110+
# math.tau since 3.6
111+
self.assertEqual(math.tau, 2*math.pi)
112+
105113
def test_ceil_basic(self):
106114
self.assertEqual(math.ceil(10), 10)
107115
self.assertEqual(math.ceil(-10), -10)
@@ -447,3 +455,29 @@ class II(int):
447455
self.assertEqual(math.ldexp(FF(10), II(12)), 40960.0)
448456
self.assertRaises(TypeError, math.ldexp, 'Hello', 1000000)
449457
self.assertRaises(TypeError, math.ldexp, 1, 'Hello')
458+
459+
def test_trunc(self):
460+
self.assertEqual(math.trunc(1), 1)
461+
self.assertEqual(math.trunc(-1), -1)
462+
self.assertEqual(type(math.trunc(1)), int)
463+
self.assertEqual(type(math.trunc(1.5)), int)
464+
self.assertEqual(math.trunc(1.5), 1)
465+
self.assertEqual(math.trunc(-1.5), -1)
466+
self.assertEqual(math.trunc(1.999999), 1)
467+
self.assertEqual(math.trunc(-1.999999), -1)
468+
self.assertEqual(math.trunc(-0.999999), -0)
469+
self.assertEqual(math.trunc(-100.999), -100)
470+
471+
class TestTrunc(object):
472+
def __trunc__(self):
473+
return 23
474+
475+
class TestNoTrunc(object):
476+
pass
477+
478+
self.assertEqual(math.trunc(TestTrunc()), 23)
479+
480+
self.assertRaises(TypeError, math.trunc)
481+
self.assertRaises(TypeError, math.trunc, 1, 2)
482+
self.assertRaises(TypeError, math.trunc, TestNoTrunc())
483+

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -698,17 +698,29 @@ Object fail(PythonClass cls, Object arg, Object keywordArg) {
698698
@Specialization(guards = "isNoValue(keywordArg)")
699699
public Object createInt(PythonClass cls, PythonObject obj, PNone keywordArg,
700700
@Cached("create(__INT__)") LookupAndCallUnaryNode callIntNode,
701+
@Cached("create(__TRUNC__)") LookupAndCallUnaryNode callTruncNode,
701702
@Cached("createBinaryProfile()") ConditionProfile isIntProfile) {
702703
try {
704+
// at first try __int__ method
703705
return createInt(cls, callIntNode.executeLong(obj), keywordArg, isIntProfile);
704706
} catch (UnexpectedResultException e) {
705707
Object result = e.getResult();
708+
if (result == PNone.NO_VALUE) {
709+
try {
710+
// now try __trunc__ method
711+
return createInt(cls, callTruncNode.executeLong(obj), keywordArg, isIntProfile);
712+
} catch (UnexpectedResultException ee) {
713+
result = ee.getResult();
714+
}
715+
}
706716
if (result == PNone.NO_VALUE) {
707717
throw raise(TypeError, "an integer is required (got type %p)", obj);
708718
} else if (result instanceof Integer) {
709719
return createInt(cls, (int) result, keywordArg);
710720
} else if (result instanceof Long) {
711721
return createInt(cls, (long) result, keywordArg, isIntProfile);
722+
} else if (result instanceof Boolean) {
723+
return createInt(cls, (boolean) result ? 1 : 0, keywordArg, isIntProfile);
712724
} else if (result instanceof PInt) {
713725
// TODO warn if 'result' not of exact Python type 'int'
714726
return isPrimitiveInt(cls) ? result : factory().createInt(cls, ((PInt) result).getValue());

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import com.oracle.graal.python.builtins.objects.tuple.PTuple;
4343
import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode;
4444
import com.oracle.graal.python.nodes.function.PythonBuiltinNode;
45+
import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
4546
import com.oracle.graal.python.nodes.truffle.PythonArithmeticTypes;
4647
import com.oracle.graal.python.runtime.exception.PythonErrorType;
4748
import com.oracle.graal.python.runtime.object.PythonObjectFactory;
@@ -68,6 +69,7 @@ public MathModuleBuiltins() {
6869
// Add constant values
6970
builtinConstants.put("pi", Math.PI);
7071
builtinConstants.put("e", Math.E);
72+
builtinConstants.put("tau", 2 * Math.PI);
7173
}
7274

7375
// math.sqrt
@@ -955,6 +957,21 @@ public abstract static class PowNode extends PythonBuiltinNode {
955957
}
956958
}
957959

960+
@Builtin(name = "trunc", fixedNumOfArguments = 1)
961+
@GenerateNodeFactory
962+
public abstract static class TruncNode extends PythonUnaryBuiltinNode {
963+
964+
@Specialization
965+
Object trunc(Object obj,
966+
@Cached("create(__TRUNC__)") LookupAndCallUnaryNode callTrunc) {
967+
Object result = callTrunc.executeObject(obj);
968+
if (result == PNone.NO_VALUE) {
969+
raise(TypeError, "type %p doesn't define __trunc__ method", obj);
970+
}
971+
return result;
972+
}
973+
}
974+
958975
@Builtin(name = "atan2", fixedNumOfArguments = 2)
959976
@GenerateNodeFactory
960977
public abstract static class Atan2Node extends PythonBuiltinNode {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/floats/FloatBuiltins.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
import static com.oracle.graal.python.nodes.SpecialMethodNames.__STR__;
5555
import static com.oracle.graal.python.nodes.SpecialMethodNames.__SUB__;
5656
import static com.oracle.graal.python.nodes.SpecialMethodNames.__TRUEDIV__;
57+
import static com.oracle.graal.python.nodes.SpecialMethodNames.__TRUNC__;
5758

5859
import java.math.BigDecimal;
5960
import java.math.RoundingMode;
@@ -83,6 +84,7 @@
8384
import com.oracle.truffle.api.dsl.NodeFactory;
8485
import com.oracle.truffle.api.dsl.Specialization;
8586
import com.oracle.truffle.api.dsl.TypeSystemReference;
87+
import com.oracle.truffle.api.profiles.ConditionProfile;
8688

8789
@CoreFunctions(extendClasses = PFloat.class)
8890
public final class FloatBuiltins extends PythonBuiltins {
@@ -924,6 +926,44 @@ static abstract class ConjugateNode extends RealNode {
924926

925927
}
926928

929+
@Builtin(name = __TRUNC__, fixedNumOfArguments = 1)
930+
@GenerateNodeFactory
931+
abstract static class TruncNode extends PythonUnaryBuiltinNode {
932+
933+
@TruffleBoundary
934+
protected static int truncate(double value) {
935+
return (int) (value < 0 ? Math.ceil(value) : Math.floor(value));
936+
}
937+
938+
@Specialization
939+
int trunc(double value,
940+
@Cached("createBinaryProfile()") ConditionProfile nanProfile,
941+
@Cached("createBinaryProfile()") ConditionProfile infProfile) {
942+
if (nanProfile.profile(Double.isNaN(value))) {
943+
throw raise(PythonErrorType.ValueError, "cannot convert float NaN to integer");
944+
}
945+
if (infProfile.profile(Double.isInfinite(value))) {
946+
throw raise(PythonErrorType.OverflowError, "cannot convert float infinity to integer");
947+
}
948+
return truncate(value);
949+
}
950+
951+
@Specialization
952+
int trunc(PFloat pValue,
953+
@Cached("createBinaryProfile()") ConditionProfile nanProfile,
954+
@Cached("createBinaryProfile()") ConditionProfile infProfile) {
955+
double value = pValue.getValue();
956+
if (nanProfile.profile(Double.isNaN(value))) {
957+
throw raise(PythonErrorType.ValueError, "cannot convert float NaN to integer");
958+
}
959+
if (infProfile.profile(Double.isInfinite(value))) {
960+
throw raise(PythonErrorType.OverflowError, "cannot convert float infinity to integer");
961+
}
962+
return truncate(value);
963+
}
964+
965+
}
966+
927967
@Builtin(name = __GETFORMAT__, fixedNumOfArguments = 2)
928968
@GenerateNodeFactory
929969
abstract static class GetFormatNode extends PythonUnaryBuiltinNode {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ints/IntBuiltins.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1888,6 +1888,12 @@ int get(@SuppressWarnings("unused") Object self) {
18881888
}
18891889
}
18901890

1891+
@GenerateNodeFactory
1892+
@Builtin(name = SpecialMethodNames.__TRUNC__, fixedNumOfArguments = 1, doc = "Truncating an Integral returns itself.")
1893+
static abstract class TruncNode extends IntNode {
1894+
1895+
}
1896+
18911897
@Builtin(name = SpecialMethodNames.__INT__, fixedNumOfArguments = 1)
18921898
@GenerateNodeFactory
18931899
abstract static class IntNode extends PythonBuiltinNode {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/SpecialMethodNames.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ public abstract class SpecialMethodNames {
8989
public static final String __DIV__ = "__div__";
9090
public static final String __MATMUL__ = "__matmul__";
9191
public static final String __TRUEDIV__ = "__truediv__";
92+
public static final String __TRUNC__ = "__trunc__";
9293
public static final String __FLOORDIV__ = "__floordiv__";
9394
public static final String __MOD__ = "__mod__";
9495
public static final String __DIVMOD__ = "__divmod__";

0 commit comments

Comments
 (0)