[Jython-checkins] jython: Correct overflow policy in complex.__abs__, math.hypot and cmath.polar
jeff.allen
jython-checkins at python.org
Wed Dec 31 02:41:09 CET 2014
https://hg.python.org/jython/rev/69826acfb4a9
changeset: 7489:69826acfb4a9
user: Jeff Allen <ja.py at farowl.co.uk>
date: Wed Dec 31 01:05:07 2014 +0000
summary:
Correct overflow policy in complex.__abs__, math.hypot and cmath.polar
Fixes test failure polar0100 in test_cmath (issue #2237), and corrects and
adds a test for the comparable behaviour on complex and math.hypot.
files:
Lib/test/test_complex_jy.py | 25 ++++++++++++
src/org/python/core/PyComplex.java | 7 ++-
src/org/python/modules/cmath.java | 6 +--
src/org/python/modules/math.java | 37 +++++++++++++----
4 files changed, 59 insertions(+), 16 deletions(-)
diff --git a/Lib/test/test_complex_jy.py b/Lib/test/test_complex_jy.py
--- a/Lib/test/test_complex_jy.py
+++ b/Lib/test/test_complex_jy.py
@@ -5,6 +5,8 @@
import unittest
from test import test_support
+INF, NINF, NAN = map(float, ("inf", "-inf", "nan"))
+
class ComplexTest(unittest.TestCase):
def test_dunder_coerce(self):
@@ -22,6 +24,29 @@
self.assertTrue(0.25+0j)
self.assertTrue(25j)
+ def test_abs_big(self):
+ # These are close to overflow but don't
+ close = [ complex( 1.794e+308, 0.000e+00),
+ complex( 1.119e+308, 1.403e+308),
+ complex(-3.992e+307, 1.749e+308),
+ complex(-1.617e+308, 7.785e+307),
+ complex(-1.617e+308,-7.785e+307),
+ complex(-3.992e+307,-1.749e+308) ]
+ # These are a little bigger and do overflow
+ over = [ complex( 1.130e+308, 1.417e+308),
+ complex(-4.032e+307, 1.767e+308),
+ complex(-1.633e+308, 7.863e+307),
+ complex(-1.633e+308,-7.863e+307),
+ complex(-4.032e+307,-1.767e+308) ]
+ # If you start with infinity, the return is infinity, no overflow
+ infinities = [ complex(INF, 1), complex(NINF, 2), complex(3, INF), complex(4, NINF) ]
+
+ for z in close :
+ self.assertAlmostEquals(abs(z), 1.794e+308, delta=0.01e+308)
+ for z in over :
+ self.assertRaises(OverflowError, abs, z)
+ for z in infinities :
+ self.assertEqual(abs(z), INF)
def test_main():
test_support.run_unittest(ComplexTest)
diff --git a/src/org/python/core/PyComplex.java b/src/org/python/core/PyComplex.java
--- a/src/org/python/core/PyComplex.java
+++ b/src/org/python/core/PyComplex.java
@@ -817,7 +817,12 @@
@ExposedMethod(doc = BuiltinDocs.complex___abs___doc)
final PyObject complex___abs__() {
- return new PyFloat(Math.hypot(real, imag));
+ double mag = Math.hypot(real, imag);
+ if (Double.isInfinite(mag) && !(Double.isInfinite(real) || Double.isInfinite(imag))) {
+ // In these circumstances Math.hypot quietly returns inf, but Python should raise.
+ throw Py.OverflowError("absolute value too large");
+ }
+ return new PyFloat(mag);
}
@Override
diff --git a/src/org/python/modules/cmath.java b/src/org/python/modules/cmath.java
--- a/src/org/python/modules/cmath.java
+++ b/src/org/python/modules/cmath.java
@@ -165,12 +165,8 @@
public static PyTuple polar(PyObject in) {
PyComplex z = complexFromPyObject(in);
- if ((Double.isInfinite(z.real) && Double.isNaN(z.imag))
- || (Double.isInfinite(z.imag) && Double.isNaN(z.real))) {
- return new PyTuple(Py.newFloat(Double.POSITIVE_INFINITY), Py.newFloat(Double.NaN));
- }
double phi = Math.atan2(z.imag, z.real);
- double r = Math.sqrt(z.real * z.real + z.imag * z.imag);
+ double r = math.hypot(z.real, z.imag);
return new PyTuple(new PyFloat(r), new PyFloat(phi));
}
diff --git a/src/org/python/modules/math.java b/src/org/python/modules/math.java
--- a/src/org/python/modules/math.java
+++ b/src/org/python/modules/math.java
@@ -84,7 +84,7 @@
*/
public static double acosh(double y) {
if (y < 1.0) {
- throw mathDomainValueError();
+ throw mathDomainError();
} else {
// acosh(y) = ln[y + sqrt(y**2 - 1)]
@@ -162,7 +162,7 @@
public static double atanh(double y) {
double absy = Math.abs(y);
if (absy >= 1.0) {
- throw mathDomainValueError();
+ throw mathDomainError();
} else {
// 2x = ln[(1+y)/(1-y)] = ln[1 + 2y/(1-y)]
double u = (absy + absy) / (1. - absy);
@@ -473,14 +473,22 @@
return checkOverflow(v * Math.pow(TWO, w));
}
- public static double hypot(double v, double w) {
- if (isinf(v) || isinf(w)) {
- return INF;
+ /**
+ * Returns (x<sup>2</sup> +y<sup>2</sup>)<sup>½</sup> without intermediate overflow or
+ * underflow. If either argument is infinite, the result is infinite, but overflow during the
+ * calculation is detected as an error.
+ *
+ * @param x
+ * @param y
+ * @return (x<sup>2</sup> +y<sup>2</sup>)<sup>½</sup>
+ */
+ public static double hypot(double x, double y) {
+ double mag = Math.hypot(x, y);
+ if (Double.isInfinite(mag) && !(Double.isInfinite(x) || Double.isInfinite(y))) {
+ // In these circumstances Math.hypot quietly returns inf, but CPython should raise.
+ throw mathRangeError();
}
- if (isnan(v) || isnan(w)) {
- return NAN;
- }
- return Math.hypot(v, w);
+ return mag;
}
public static double radians(double v) {
@@ -609,10 +617,19 @@
*
* @return ValueError("math domain error")
*/
- private static PyException mathDomainValueError() {
+ private static PyException mathDomainError() {
return Py.ValueError("math domain error");
}
+ /**
+ * Returns a OverflowError("math range error"), ready to throw from the client code.
+ *
+ * @return OverflowError("math range error")
+ */
+ private static PyException mathRangeError() {
+ return Py.OverflowError("math range error");
+ }
+
private static void throwMathDomainValueError() {
throw Py.ValueError("math domain error");
}
--
Repository URL: https://hg.python.org/jython
More information about the Jython-checkins
mailing list