Skip to content

Commit aa64dad

Browse files
committed
[GR-10427] Math.pow is not fully implemented.
1 parent 55293b9 commit aa64dad

File tree

2 files changed

+253
-3
lines changed

2 files changed

+253
-3
lines changed

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

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,157 @@ def test_isnan(self):
423423
self.assertFalse(math.isnan(False))
424424
self.assertFalse(math.isnan(MyFloat()))
425425

426+
def testPow(self):
427+
self.assertRaises(TypeError, math.pow)
428+
self.ftest('pow(0,1)', math.pow(0,1), 0)
429+
self.ftest('pow(1,0)', math.pow(1,0), 1)
430+
self.ftest('pow(2,1)', math.pow(2,1), 2)
431+
self.ftest('pow(2,-1)', math.pow(2,-1), 0.5)
432+
self.assertEqual(math.pow(INF, 1), INF)
433+
self.assertEqual(math.pow(NINF, 1), NINF)
434+
self.assertEqual((math.pow(1, INF)), 1.)
435+
self.assertEqual((math.pow(1, NINF)), 1.)
436+
self.assertTrue(math.isnan(math.pow(NAN, 1)))
437+
self.assertTrue(math.isnan(math.pow(2, NAN)))
438+
self.assertTrue(math.isnan(math.pow(0, NAN)))
439+
self.assertEqual(math.pow(1, NAN), 1)
440+
441+
# pow(0., x)
442+
self.assertEqual(math.pow(0., INF), 0.)
443+
self.assertEqual(math.pow(0., 3.), 0.)
444+
self.assertEqual(math.pow(0., 2.3), 0.)
445+
self.assertEqual(math.pow(0., 2.), 0.)
446+
self.assertEqual(math.pow(0., 0.), 1.)
447+
self.assertEqual(math.pow(0., -0.), 1.)
448+
self.assertRaises(ValueError, math.pow, 0., -2.)
449+
self.assertRaises(ValueError, math.pow, 0., -2.3)
450+
self.assertRaises(ValueError, math.pow, 0., -3.)
451+
self.assertRaises(ValueError, math.pow, 0., NINF)
452+
self.assertTrue(math.isnan(math.pow(0., NAN)))
453+
454+
# pow(INF, x)
455+
self.assertEqual(math.pow(INF, INF), INF)
456+
self.assertEqual(math.pow(INF, 3.), INF)
457+
self.assertEqual(math.pow(INF, 2.3), INF)
458+
self.assertEqual(math.pow(INF, 2.), INF)
459+
self.assertEqual(math.pow(INF, 0.), 1.)
460+
self.assertEqual(math.pow(INF, -0.), 1.)
461+
self.assertEqual(math.pow(INF, -2.), 0.)
462+
self.assertEqual(math.pow(INF, -2.3), 0.)
463+
self.assertEqual(math.pow(INF, -3.), 0.)
464+
self.assertEqual(math.pow(INF, NINF), 0.)
465+
self.assertTrue(math.isnan(math.pow(INF, NAN)))
466+
467+
# pow(-0., x)
468+
self.assertEqual(math.pow(-0., INF), 0.)
469+
self.assertEqual(math.pow(-0., 3.), -0.)
470+
self.assertEqual(math.pow(-0., 2.3), 0.)
471+
self.assertEqual(math.pow(-0., 2.), 0.)
472+
self.assertEqual(math.pow(-0., 0.), 1.)
473+
self.assertEqual(math.pow(-0., -0.), 1.)
474+
self.assertRaises(ValueError, math.pow, -0., -2.)
475+
self.assertRaises(ValueError, math.pow, -0., -2.3)
476+
self.assertRaises(ValueError, math.pow, -0., -3.)
477+
self.assertRaises(ValueError, math.pow, -0., NINF)
478+
self.assertTrue(math.isnan(math.pow(-0., NAN)))
479+
480+
# pow(NINF, x)
481+
self.assertEqual(math.pow(NINF, INF), INF)
482+
self.assertEqual(math.pow(NINF, 3.), NINF)
483+
self.assertEqual(math.pow(NINF, 2.3), INF)
484+
self.assertEqual(math.pow(NINF, 2.), INF)
485+
self.assertEqual(math.pow(NINF, 0.), 1.)
486+
self.assertEqual(math.pow(NINF, -0.), 1.)
487+
self.assertEqual(math.pow(NINF, -2.), 0.)
488+
self.assertEqual(math.pow(NINF, -2.3), 0.)
489+
self.assertEqual(math.pow(NINF, -3.), -0.)
490+
self.assertEqual(math.pow(NINF, NINF), 0.)
491+
self.assertTrue(math.isnan(math.pow(NINF, NAN)))
492+
493+
# pow(-1, x)
494+
self.assertEqual(math.pow(-1., INF), 1.)
495+
self.assertEqual(math.pow(-1., 3.), -1.)
496+
self.assertRaises(ValueError, math.pow, -1., 2.3)
497+
self.assertEqual(math.pow(-1., 2.), 1.)
498+
self.assertEqual(math.pow(-1., 0.), 1.)
499+
self.assertEqual(math.pow(-1., -0.), 1.)
500+
self.assertEqual(math.pow(-1., -2.), 1.)
501+
self.assertRaises(ValueError, math.pow, -1., -2.3)
502+
self.assertEqual(math.pow(-1., -3.), -1.)
503+
self.assertEqual(math.pow(-1., NINF), 1.)
504+
self.assertTrue(math.isnan(math.pow(-1., NAN)))
505+
506+
# pow(1, x)
507+
self.assertEqual(math.pow(1., INF), 1.)
508+
self.assertEqual(math.pow(1., 3.), 1.)
509+
self.assertEqual(math.pow(1., 2.3), 1.)
510+
self.assertEqual(math.pow(1., 2.), 1.)
511+
self.assertEqual(math.pow(1., 0.), 1.)
512+
self.assertEqual(math.pow(1., -0.), 1.)
513+
self.assertEqual(math.pow(1., -2.), 1.)
514+
self.assertEqual(math.pow(1., -2.3), 1.)
515+
self.assertEqual(math.pow(1., -3.), 1.)
516+
self.assertEqual(math.pow(1., NINF), 1.)
517+
self.assertEqual(math.pow(1., NAN), 1.)
518+
519+
# pow(x, 0) should be 1 for any x
520+
self.assertEqual(math.pow(2.3, 0.), 1.)
521+
self.assertEqual(math.pow(-2.3, 0.), 1.)
522+
self.assertEqual(math.pow(NAN, 0.), 1.)
523+
self.assertEqual(math.pow(2.3, -0.), 1.)
524+
self.assertEqual(math.pow(-2.3, -0.), 1.)
525+
self.assertEqual(math.pow(NAN, -0.), 1.)
526+
527+
# pow(x, y) is invalid if x is negative and y is not integral
528+
self.assertRaises(ValueError, math.pow, -1., 2.3)
529+
self.assertRaises(ValueError, math.pow, -15., -3.1)
530+
531+
# pow(x, NINF)
532+
self.assertEqual(math.pow(1.9, NINF), 0.)
533+
self.assertEqual(math.pow(1.1, NINF), 0.)
534+
self.assertEqual(math.pow(0.9, NINF), INF)
535+
self.assertEqual(math.pow(0.1, NINF), INF)
536+
self.assertEqual(math.pow(-0.1, NINF), INF)
537+
self.assertEqual(math.pow(-0.9, NINF), INF)
538+
self.assertEqual(math.pow(-1.1, NINF), 0.)
539+
self.assertEqual(math.pow(-1.9, NINF), 0.)
540+
541+
# pow(x, INF)
542+
self.assertEqual(math.pow(1.9, INF), INF)
543+
self.assertEqual(math.pow(1.1, INF), INF)
544+
self.assertEqual(math.pow(0.9, INF), 0.)
545+
self.assertEqual(math.pow(0.1, INF), 0.)
546+
self.assertEqual(math.pow(-0.1, INF), 0.)
547+
self.assertEqual(math.pow(-0.9, INF), 0.)
548+
self.assertEqual(math.pow(-1.1, INF), INF)
549+
self.assertEqual(math.pow(-1.9, INF), INF)
550+
551+
# pow(x, y) should work for x negative, y an integer
552+
self.ftest('(-2.)**3.', math.pow(-2.0, 3.0), -8.0)
553+
self.ftest('(-2.)**2.', math.pow(-2.0, 2.0), 4.0)
554+
self.ftest('(-2.)**1.', math.pow(-2.0, 1.0), -2.0)
555+
self.ftest('(-2.)**0.', math.pow(-2.0, 0.0), 1.0)
556+
self.ftest('(-2.)**-0.', math.pow(-2.0, -0.0), 1.0)
557+
self.ftest('(-2.)**-1.', math.pow(-2.0, -1.0), -0.5)
558+
self.ftest('(-2.)**-2.', math.pow(-2.0, -2.0), 0.25)
559+
self.ftest('(-2.)**-3.', math.pow(-2.0, -3.0), -0.125)
560+
self.assertRaises(ValueError, math.pow, -2.0, -0.5)
561+
self.assertRaises(ValueError, math.pow, -2.0, 0.5)
562+
563+
self.assertRaises(OverflowError, math.pow, 999999999999999999999999999, 999999999999999999999999999)
564+
565+
# testing specializations
566+
self.assertEqual(math.pow(0, 999999999999999999999999999), 0)
567+
self.assertEqual(math.pow(999999999999999999999999999, 0), 1)
568+
self.assertEqual(math.pow(0.0, 999999999999999999999999999), 0)
569+
self.assertEqual(math.pow(999999999999999999999999999, 0.0), 1)
570+
571+
class MyNumber():
572+
def __float__(self):
573+
return -2.;
574+
self.ftest('MyFloat()**-3.', math.pow(MyNumber(), -3.0), -0.125)
575+
576+
426577
def test_fabs(self):
427578
self.assertEqual(math.fabs(-1), 1)
428579
self.assertEqual(math.fabs(0), 0)

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

Lines changed: 102 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import com.oracle.graal.python.nodes.SpecialMethodNames;
4646
import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode;
4747
import com.oracle.graal.python.nodes.function.PythonBuiltinNode;
48+
import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode;
4849
import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
4950
import com.oracle.graal.python.nodes.truffle.PythonArithmeticTypes;
5051
import com.oracle.graal.python.runtime.exception.PythonErrorType;
@@ -1412,23 +1413,121 @@ public double fabs(Object value) {
14121413
}
14131414

14141415
@Builtin(name = "pow", fixedNumOfArguments = 2)
1416+
@TypeSystemReference(PythonArithmeticTypes.class)
1417+
@ImportStatic(MathGuards.class)
14151418
@GenerateNodeFactory
1416-
public abstract static class PowNode extends PythonBuiltinNode {
1419+
public abstract static class PowNode extends PythonBinaryBuiltinNode {
1420+
1421+
public abstract double executeObject(Object left, Object right);
14171422

14181423
@Specialization
1419-
double pow(int left, int right) {
1424+
double pow(long left, long right) {
14201425
return pow((double) left, (double) right);
14211426
}
14221427

1428+
@Specialization
1429+
double pow(double left, long right) {
1430+
return pow(left, (double) right);
1431+
}
1432+
1433+
@Specialization
1434+
double pow(long left, double right) {
1435+
return pow((double) left, right);
1436+
}
1437+
14231438
@Specialization
14241439
double pow(PInt left, PInt right) {
14251440
return pow(left.doubleValue(), right.doubleValue());
14261441
}
14271442

1443+
@Specialization
1444+
double pow(long left, PInt right) {
1445+
return pow((double) left, right.doubleValue());
1446+
}
1447+
1448+
@Specialization
1449+
double pow(PInt left, long right) {
1450+
return pow(left.doubleValue(), (double) right);
1451+
}
1452+
1453+
@Specialization
1454+
double pow(double left, PInt right) {
1455+
return pow(left, right.doubleValue());
1456+
}
1457+
1458+
@Specialization
1459+
double pow(PInt left, double right) {
1460+
return pow(left.doubleValue(), right);
1461+
}
1462+
14281463
@TruffleBoundary
14291464
@Specialization
14301465
double pow(double left, double right) {
1431-
return Math.pow(left, right);
1466+
double result = 0;
1467+
if (!Double.isFinite(left) || !Double.isFinite(right)) {
1468+
if (Double.isNaN(left)) {
1469+
result = right == 0 ? 1 : left;
1470+
} else if (Double.isNaN(right)) {
1471+
result = left == 1 ? 1 : right;
1472+
} else if (Double.isInfinite(left)) {
1473+
boolean oddRight = Double.isFinite(right) && (Math.abs(right) % 2.0) == 1;
1474+
if (right > 0) {
1475+
result = oddRight ? left : Math.abs(left);
1476+
} else if (right == 0) {
1477+
result = 1;
1478+
} else {
1479+
result = oddRight ? Math.copySign(0., left) : 0;
1480+
}
1481+
} else if (Double.isInfinite(right)) {
1482+
if (Math.abs(left) == 1) {
1483+
result = 1;
1484+
} else if (right > 0 && Math.abs(left) > 1) {
1485+
result = right;
1486+
} else if (right < 0 && Math.abs(left) < 1) {
1487+
result = -right;
1488+
if (left == 0) {
1489+
throw raise(ValueError, "math domain error");
1490+
}
1491+
} else {
1492+
result = 0;
1493+
}
1494+
}
1495+
} else {
1496+
result = Math.pow(left, right);
1497+
if (!Double.isFinite(result)) {
1498+
if (Double.isNaN(result)) {
1499+
throw raise(ValueError, "math domain error");
1500+
} else if (Double.isInfinite(result)) {
1501+
if (left == 0) {
1502+
throw raise(ValueError, "math domain error");
1503+
} else {
1504+
throw raise(OverflowError, "math range error");
1505+
}
1506+
}
1507+
}
1508+
}
1509+
return result;
1510+
}
1511+
1512+
@Specialization(guards = {"!isNumber(left)||!isNumber(right)"})
1513+
double pow(Object left, Object right,
1514+
@Cached("create(__FLOAT__)") LookupAndCallUnaryNode dispatchLeftFloat,
1515+
@Cached("create(__FLOAT__)") LookupAndCallUnaryNode dispatchRightFloat,
1516+
@Cached("create()") PowNode powNode) {
1517+
Object leftFloat = dispatchLeftFloat.executeObject(left);
1518+
if (leftFloat == PNone.NO_VALUE) {
1519+
throw raise(TypeError, "must be real number, not %p", left);
1520+
}
1521+
Object rightFloat = dispatchLeftFloat.executeObject(right);
1522+
if (leftFloat == PNone.NO_VALUE) {
1523+
throw raise(TypeError, "must be real number, not %p", right);
1524+
}
1525+
1526+
return powNode.executeObject(leftFloat, rightFloat);
1527+
}
1528+
1529+
public static PowNode create() {
1530+
return MathModuleBuiltinsFactory.PowNodeFactory.create(new PNode[0]);
14321531
}
14331532
}
14341533

0 commit comments

Comments
 (0)