[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