Skip to content

Commit 4587357

Browse files
committed
Fix converting infinite floats to longs
1 parent 33a3d0a commit 4587357

File tree

7 files changed

+155
-121
lines changed

7 files changed

+155
-121
lines changed

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1026,9 +1026,10 @@ class TestNoFloor:
10261026
# This fails on some platforms - so check it here
10271027
self.ftest('floor(1.23e167)', math.floor(1.23e167), 1.23e167)
10281028
self.ftest('floor(-1.23e167)', math.floor(-1.23e167), -1.23e167)
1029-
#self.assertEqual(math.ceil(INF), INF)
1030-
#self.assertEqual(math.ceil(NINF), NINF)
1031-
#self.assertTrue(math.isnan(math.floor(NAN)))
1029+
for fn in (math.floor, math.ceil):
1030+
self.assertRaises(OverflowError, fn, INF)
1031+
self.assertRaises(OverflowError, fn, NINF)
1032+
self.assertRaises(ValueError, fn, NAN)
10321033

10331034
t = TestNoFloor()
10341035
t.__floor__ = lambda *args: args
@@ -1046,7 +1047,7 @@ class TestNoFloor:
10461047
class MyFloorFloat():
10471048
def __floor__(self):
10481049
return 12
1049-
def __float(self):
1050+
def __float__(self):
10501051
return 112
10511052
self.assertEqual(math.floor(MyFloorFloat()), 12)
10521053

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

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,6 @@
138138
import com.oracle.graal.python.builtins.objects.dict.PDict;
139139
import com.oracle.graal.python.builtins.objects.ellipsis.PEllipsis;
140140
import com.oracle.graal.python.builtins.objects.enumerate.PEnumerate;
141-
import com.oracle.graal.python.builtins.objects.floats.FloatBuiltins;
142-
import com.oracle.graal.python.builtins.objects.floats.FloatBuiltinsFactory;
143141
import com.oracle.graal.python.builtins.objects.floats.FloatUtils;
144142
import com.oracle.graal.python.builtins.objects.floats.PFloat;
145143
import com.oracle.graal.python.builtins.objects.frame.PFrame;
@@ -185,6 +183,7 @@
185183
import com.oracle.graal.python.lib.PyFloatAsDoubleNode;
186184
import com.oracle.graal.python.lib.PyFloatFromString;
187185
import com.oracle.graal.python.lib.PyIndexCheckNode;
186+
import com.oracle.graal.python.lib.PyLongFromDoubleNode;
188187
import com.oracle.graal.python.lib.PyMappingCheckNode;
189188
import com.oracle.graal.python.lib.PyMemoryViewFromObject;
190189
import com.oracle.graal.python.lib.PyNumberAsSizeNode;
@@ -1414,14 +1413,15 @@ Object createInt(Object cls, long arg, @SuppressWarnings("unused") PNone base,
14141413
}
14151414

14161415
@Specialization(guards = "isNoValue(base)")
1416+
@SuppressWarnings("truffle-static-method")
14171417
Object createInt(Object cls, double arg, @SuppressWarnings("unused") PNone base,
14181418
@Bind("this") Node inliningTarget,
14191419
@Shared("primitiveInt") @Cached InlineIsBuiltinClassProfile isPrimitiveIntProfile,
1420-
@Cached("createFloatInt()") FloatBuiltins.IntNode floatToIntNode,
1420+
@Cached PyLongFromDoubleNode pyLongFromDoubleNode,
14211421
@Shared @Cached InlinedBranchProfile bigIntegerProfile,
14221422
@Shared @Cached InlinedBranchProfile primitiveIntProfile,
14231423
@Shared @Cached InlinedBranchProfile fullIntProfile) {
1424-
Object result = floatToIntNode.executeWithDouble(arg);
1424+
Object result = pyLongFromDoubleNode.execute(inliningTarget, arg);
14251425
return createInt(cls, result, inliningTarget, isPrimitiveIntProfile, bigIntegerProfile, primitiveIntProfile, fullIntProfile);
14261426
}
14271427

@@ -1689,11 +1689,6 @@ protected static boolean isHandledType(Object obj) {
16891689
return PGuards.isInteger(obj) || obj instanceof Double || obj instanceof Boolean || PGuards.isString(obj) || PGuards.isBytes(obj) || obj instanceof PythonNativeVoidPtr;
16901690
}
16911691

1692-
@NeverDefault
1693-
protected static FloatBuiltins.IntNode createFloatInt() {
1694-
return FloatBuiltinsFactory.IntNodeFactory.create();
1695-
}
1696-
16971692
private Object callIndex(VirtualFrame frame, Object obj) {
16981693
if (callIndexNode == null) {
16991694
CompilerDirectives.transferToInterpreterAndInvalidate();

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

Lines changed: 36 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,17 @@
5353
import com.oracle.graal.python.builtins.objects.type.SpecialMethodSlot;
5454
import com.oracle.graal.python.lib.PyFloatAsDoubleNode;
5555
import com.oracle.graal.python.lib.PyLongAsLongAndOverflowNode;
56+
import com.oracle.graal.python.lib.PyLongFromDoubleNode;
5657
import com.oracle.graal.python.lib.PyNumberAsSizeNode;
5758
import com.oracle.graal.python.lib.PyNumberIndexNode;
5859
import com.oracle.graal.python.lib.PyObjectGetIter;
5960
import com.oracle.graal.python.nodes.ErrorMessages;
6061
import com.oracle.graal.python.nodes.PGuards;
6162
import com.oracle.graal.python.nodes.PNodeWithRaise;
6263
import com.oracle.graal.python.nodes.builtins.TupleNodes;
64+
import com.oracle.graal.python.nodes.call.special.CallUnaryMethodNode;
6365
import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode;
66+
import com.oracle.graal.python.nodes.call.special.LookupSpecialMethodNode;
6467
import com.oracle.graal.python.nodes.classes.IsSubtypeNode;
6568
import com.oracle.graal.python.nodes.expression.BinaryArithmetic;
6669
import com.oracle.graal.python.nodes.expression.BinaryComparisonNode;
@@ -76,6 +79,7 @@
7679
import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentClinicProvider;
7780
import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile;
7881
import com.oracle.graal.python.nodes.object.GetClassNode;
82+
import com.oracle.graal.python.nodes.object.InlinedGetClassNode;
7983
import com.oracle.graal.python.nodes.truffle.PythonArithmeticTypes;
8084
import com.oracle.graal.python.nodes.util.CastToJavaLongLossyNode;
8185
import com.oracle.graal.python.nodes.util.NarrowBigIntegerNode;
@@ -248,49 +252,31 @@ public double count(double value) {
248252
}
249253

250254
@Builtin(name = "ceil", minNumOfPositionalArgs = 1)
251-
@ImportStatic(MathGuards.class)
252255
@GenerateNodeFactory
253256
public abstract static class CeilNode extends MathUnaryBuiltinNode {
254-
public abstract Object execute(VirtualFrame frame, double value);
255-
256-
@Specialization(guards = {"fitLong(value)"})
257-
public long ceilLong(double value) {
258-
return (long) Math.ceil(value);
259-
}
260-
261-
@Specialization(guards = {"!fitLong(value)"})
262-
@TruffleBoundary
263-
public PInt ceil(double value) {
264-
return factory().createInt(BigDecimal.valueOf(Math.ceil(value)).toBigInteger());
265-
}
266-
267-
@Specialization
268-
public int ceil(int value) {
269-
return value;
270-
}
271-
272-
@Specialization
273-
public long ceil(long value) {
274-
return value;
275-
}
276257

277258
@Specialization
278-
public int ceil(boolean value) {
279-
return value ? 1 : 0;
259+
static Object ceilDouble(double value,
260+
@Bind("this") Node inliningTarget,
261+
@Shared @Cached PyLongFromDoubleNode pyLongFromDoubleNode) {
262+
return pyLongFromDoubleNode.execute(inliningTarget, Math.ceil(value));
280263
}
281264

282-
@Specialization
283-
public Object ceil(VirtualFrame frame, Object value,
265+
@Fallback
266+
static Object ceil(VirtualFrame frame, Object value,
267+
@Bind("this") Node inliningTarget,
268+
@Cached InlinedGetClassNode getClassNode,
269+
@Cached("create(T___CEIL__)") LookupSpecialMethodNode lookupCeil,
270+
@Cached CallUnaryMethodNode callCeil,
284271
@Cached PyFloatAsDoubleNode asDoubleNode,
285-
@Cached("create(T___CEIL__)") LookupAndCallUnaryNode dispatchCeil,
286-
@Cached CeilNode recursive) {
287-
Object result = dispatchCeil.executeObject(frame, value);
288-
if (result == PNone.NO_VALUE) {
289-
return recursive.execute(frame, asDoubleNode.execute(frame, value));
272+
@Shared @Cached PyLongFromDoubleNode pyLongFromDoubleNode) {
273+
Object method = lookupCeil.execute(frame, getClassNode.execute(inliningTarget, value), value);
274+
if (method != PNone.NO_VALUE) {
275+
return callCeil.executeObject(frame, method, value);
290276
}
291-
return result;
277+
double doubleValue = asDoubleNode.execute(frame, value);
278+
return pyLongFromDoubleNode.execute(inliningTarget, Math.ceil(doubleValue));
292279
}
293-
294280
}
295281

296282
@Builtin(name = "copysign", minNumOfPositionalArgs = 2, numOfPositionalOnlyArgs = 2, parameterNames = {"magnitude", "sign"})
@@ -522,51 +508,30 @@ Object perm(VirtualFrame frame, Object n, Object k,
522508

523509
@Builtin(name = "floor", minNumOfPositionalArgs = 1)
524510
@GenerateNodeFactory
525-
@ImportStatic(MathGuards.class)
526511
public abstract static class FloorNode extends PythonUnaryBuiltinNode {
527-
public abstract Object execute(VirtualFrame frame, double value);
528-
529-
@Specialization(guards = {"fitLong(value)"})
530-
public long floorDL(double value) {
531-
return (long) Math.floor(value);
532-
}
533-
534-
@Specialization(guards = {"!fitLong(value)"})
535-
@TruffleBoundary
536-
public PInt floorD(double value) {
537-
return factory().createInt(BigDecimal.valueOf(Math.floor(value)).toBigInteger());
538-
}
539512

540513
@Specialization
541-
public int floorI(int value) {
542-
return value;
543-
}
544-
545-
@Specialization
546-
public long floorL(long value) {
547-
return value;
548-
}
549-
550-
@Specialization
551-
public int floorB(boolean value) {
552-
if (value) {
553-
return 1;
554-
}
555-
return 0;
514+
static Object floorDouble(double value,
515+
@Bind("this") Node inliningTarget,
516+
@Shared @Cached PyLongFromDoubleNode pyLongFromDoubleNode) {
517+
return pyLongFromDoubleNode.execute(inliningTarget, Math.floor(value));
556518
}
557519

558-
@Specialization
559-
public Object floor(VirtualFrame frame, Object value,
560-
@Cached("create(T___FLOOR__)") LookupAndCallUnaryNode dispatchFloor,
520+
@Fallback
521+
static Object floor(VirtualFrame frame, Object value,
522+
@Bind("this") Node inliningTarget,
523+
@Cached InlinedGetClassNode getClassNode,
524+
@Cached("create(T___FLOOR__)") LookupSpecialMethodNode lookupFloor,
525+
@Cached CallUnaryMethodNode callFloor,
561526
@Cached PyFloatAsDoubleNode asDoubleNode,
562-
@Cached FloorNode recursiveNode) {
563-
Object result = dispatchFloor.executeObject(frame, value);
564-
if (PNone.NO_VALUE == result) {
565-
return recursiveNode.execute(frame, asDoubleNode.execute(frame, value));
527+
@Shared @Cached PyLongFromDoubleNode pyLongFromDoubleNode) {
528+
Object method = lookupFloor.execute(frame, getClassNode.execute(inliningTarget, value), value);
529+
if (method != PNone.NO_VALUE) {
530+
return callFloor.executeObject(frame, method, value);
566531
}
567-
return result;
532+
double doubleValue = asDoubleNode.execute(frame, value);
533+
return pyLongFromDoubleNode.execute(inliningTarget, Math.floor(doubleValue));
568534
}
569-
570535
}
571536

572537
@Builtin(name = "fmod", minNumOfPositionalArgs = 2, numOfPositionalOnlyArgs = 2, parameterNames = {"left", "right"})

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextLongBuiltins.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,9 @@
6969
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor;
7070
import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.ConvertPIntToPrimitiveNode;
7171
import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodesFactory.ConvertPIntToPrimitiveNodeGen;
72-
import com.oracle.graal.python.builtins.objects.floats.FloatBuiltins.IntNode;
7372
import com.oracle.graal.python.builtins.objects.ints.IntBuiltins.NegNode;
7473
import com.oracle.graal.python.builtins.objects.ints.PInt;
74+
import com.oracle.graal.python.lib.PyLongFromDoubleNode;
7575
import com.oracle.graal.python.nodes.ErrorMessages;
7676
import com.oracle.graal.python.nodes.classes.IsSubtypeNode;
7777
import com.oracle.graal.python.nodes.object.GetClassNode;
@@ -80,13 +80,15 @@
8080
import com.oracle.graal.python.util.OverflowException;
8181
import com.oracle.truffle.api.CompilerDirectives;
8282
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
83+
import com.oracle.truffle.api.dsl.Bind;
8384
import com.oracle.truffle.api.dsl.Cached;
8485
import com.oracle.truffle.api.dsl.Fallback;
8586
import com.oracle.truffle.api.dsl.Specialization;
8687
import com.oracle.truffle.api.dsl.TypeSystemReference;
8788
import com.oracle.truffle.api.interop.InteropLibrary;
8889
import com.oracle.truffle.api.interop.UnsupportedMessageException;
8990
import com.oracle.truffle.api.library.CachedLibrary;
91+
import com.oracle.truffle.api.nodes.Node;
9092
import com.oracle.truffle.api.nodes.UnexpectedResultException;
9193
import com.oracle.truffle.api.profiles.BranchProfile;
9294
import com.oracle.truffle.api.strings.TruffleString;
@@ -186,8 +188,9 @@ abstract static class PyLong_FromDouble extends CApiUnaryBuiltinNode {
186188

187189
@Specialization
188190
static Object fromDouble(double d,
189-
@Cached IntNode intNode) {
190-
return intNode.execute(null, d);
191+
@Bind("this") Node inliningTarget,
192+
@Cached PyLongFromDoubleNode pyLongFromDoubleNode) {
193+
return pyLongFromDoubleNode.execute(inliningTarget, d);
191194
}
192195
}
193196

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

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,16 @@
2626
package com.oracle.graal.python.builtins.objects.floats;
2727

2828
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.OverflowError;
29-
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.ValueError;
3029
import static com.oracle.graal.python.nodes.BuiltinNames.J_FLOAT;
3130
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___ABS__;
3231
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___ADD__;
3332
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___BOOL__;
33+
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___CEIL__;
3434
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___DIVMOD__;
3535
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___EQ__;
3636
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___FLOAT__;
3737
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___FLOORDIV__;
38+
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___FLOOR__;
3839
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___FORMAT__;
3940
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___GETFORMAT__;
4041
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___GETNEWARGS__;
@@ -70,7 +71,6 @@
7071

7172
import java.math.BigDecimal;
7273
import java.math.BigInteger;
73-
import java.math.MathContext;
7474
import java.math.RoundingMode;
7575
import java.nio.ByteOrder;
7676
import java.util.List;
@@ -91,6 +91,7 @@
9191
import com.oracle.graal.python.builtins.objects.floats.FloatBuiltinsClinicProviders.FormatNodeClinicProviderGen;
9292
import com.oracle.graal.python.builtins.objects.ints.PInt;
9393
import com.oracle.graal.python.builtins.objects.tuple.PTuple;
94+
import com.oracle.graal.python.lib.PyLongFromDoubleNode;
9495
import com.oracle.graal.python.lib.PyObjectHashNode;
9596
import com.oracle.graal.python.nodes.ErrorMessages;
9697
import com.oracle.graal.python.nodes.call.special.LookupAndCallTernaryNode;
@@ -224,39 +225,14 @@ static boolean bool(double self) {
224225
@Builtin(name = J___INT__, minNumOfPositionalArgs = 1)
225226
@Builtin(name = J___TRUNC__, minNumOfPositionalArgs = 1)
226227
@GenerateNodeFactory
227-
@ImportStatic(MathGuards.class)
228228
@TypeSystemReference(PythonArithmeticTypes.class)
229-
public abstract static class IntNode extends PythonUnaryBuiltinNode {
229+
abstract static class IntNode extends PythonUnaryBuiltinNode {
230230

231-
public abstract Object executeWithDouble(double self);
232-
233-
@Specialization(guards = "fitInt(self)")
234-
static int doIntRange(double self) {
235-
return (int) self;
236-
}
237-
238-
@Specialization(guards = "fitLong(self)")
239-
static long doLongRange(double self) {
240-
return (long) self;
241-
}
242-
243-
@Specialization(guards = "!fitLong(self)", rewriteOn = NumberFormatException.class)
244-
PInt doDoubleGeneric(double self) {
245-
return factory().createInt(fromDouble(self));
246-
}
247-
248-
@Specialization(guards = "!fitLong(self)", replaces = "doDoubleGeneric")
249-
PInt doDoubleGenericError(double self) {
250-
try {
251-
return factory().createInt(fromDouble(self));
252-
} catch (NumberFormatException e) {
253-
throw raise(Double.isNaN(self) ? ValueError : OverflowError, ErrorMessages.CANNOT_CONVERT_FLOAT_F_TO_INT, self);
254-
}
255-
}
256-
257-
@TruffleBoundary(transferToInterpreterOnException = false)
258-
private static BigInteger fromDouble(double self) {
259-
return new BigDecimal(self, MathContext.UNLIMITED).toBigInteger();
231+
@Specialization
232+
static Object doDouble(double self,
233+
@Bind("this") Node inliningTarget,
234+
@Cached PyLongFromDoubleNode pyLongFromDoubleNode) {
235+
return pyLongFromDoubleNode.execute(inliningTarget, self);
260236
}
261237
}
262238

@@ -1624,8 +1600,32 @@ static double neg(double arg) {
16241600
}
16251601
}
16261602

1603+
@Builtin(name = J___FLOOR__, minNumOfPositionalArgs = 1)
16271604
@GenerateNodeFactory
1605+
@TypeSystemReference(PythonArithmeticTypes.class)
1606+
abstract static class FloorNode extends PythonUnaryBuiltinNode {
1607+
@Specialization
1608+
static Object floor(double self,
1609+
@Bind("this") Node inliningTarget,
1610+
@Cached PyLongFromDoubleNode pyLongFromDoubleNode) {
1611+
return pyLongFromDoubleNode.execute(inliningTarget, Math.floor(self));
1612+
}
1613+
}
1614+
1615+
@Builtin(name = J___CEIL__, minNumOfPositionalArgs = 1)
1616+
@GenerateNodeFactory
1617+
@TypeSystemReference(PythonArithmeticTypes.class)
1618+
abstract static class CeilNode extends PythonUnaryBuiltinNode {
1619+
@Specialization
1620+
static Object ceil(double self,
1621+
@Bind("this") Node inliningTarget,
1622+
@Cached PyLongFromDoubleNode pyLongFromDoubleNode) {
1623+
return pyLongFromDoubleNode.execute(inliningTarget, Math.ceil(self));
1624+
}
1625+
}
1626+
16281627
@Builtin(name = "real", minNumOfPositionalArgs = 1, isGetter = true, doc = "the real part of a complex number")
1628+
@GenerateNodeFactory
16291629
abstract static class RealNode extends PythonBuiltinNode {
16301630

16311631
@Specialization
@@ -1799,7 +1799,7 @@ static boolean isInteger(double value) {
17991799
}
18001800

18011801
@Specialization
1802-
static boolean trunc(PFloat pValue) {
1802+
static boolean isInteger(PFloat pValue) {
18031803
return isInteger(pValue.getValue());
18041804
}
18051805

0 commit comments

Comments
 (0)