[Jython-checkins] jython: cmath.exp conforms to test_cmath
jeff.allen
jython-checkins at python.org
Tue Jan 20 23:07:15 CET 2015
https://hg.python.org/jython/rev/367557fb6120
changeset: 7543:367557fb6120
parent: 7510:44cfecd93257
user: Jeff Allen <ja.py at farowl.co.uk>
date: Thu Jan 08 19:37:26 2015 +0000
summary:
cmath.exp conforms to test_cmath
... and in the remotest corners of the complex plane.
files:
src/org/python/modules/cmath.java | 108 +++++++++++++++++-
src/org/python/modules/math.java | 6 +-
2 files changed, 107 insertions(+), 7 deletions(-)
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
@@ -2,6 +2,7 @@
import org.python.core.Py;
import org.python.core.PyComplex;
+import org.python.core.PyException;
import org.python.core.PyFloat;
import org.python.core.PyInstance;
import org.python.core.PyObject;
@@ -19,6 +20,8 @@
/** 2<sup>-½</sup> (Ref: Abramowitz & Stegun [1972], p2). */
private static final double ROOT_HALF = 0.70710678118654752440;
+ /** ln({@link Double#MAX_VALUE}) or a little less */
+ private static final double NEARLY_LN_DBL_MAX = 709.4361393;
private static PyComplex c_prodi(PyComplex x) {
return (PyComplex)x.__mul__(i);
@@ -140,10 +143,65 @@
* math.sinh(x.real));
}
- public static PyComplex exp(PyObject in) {
- PyComplex x = complexFromPyObject(in);
- double l = Math.exp(x.real);
- return new PyComplex(l * Math.cos(x.imag), l * Math.sin(x.imag));
+ /**
+ * Return the exponential value e<sup>z</sup>.
+ *
+ * @param z
+ * @return e<sup>z</sup>
+ */
+ public static PyComplex exp(PyObject z) {
+ PyComplex zz = complexFromPyObject(z);
+ double x = zz.real, y = zz.imag, r, u, v;
+ /*
+ * This has a lot of corner-cases, and some of them make little sense sense, but it matches
+ * CPython and passes the regression tests.
+ */
+ if (y == 0.) {
+ // Real value: use a real solution. (This may raise a range error.)
+ u = math.exp(x);
+ // v follows sign of y.
+ v = y;
+
+ } else {
+ // The trig calls will not throw, although if y is infinite, they return nan.
+ double cosy = Math.cos(y), siny = Math.sin(y);
+
+ if (x == Double.NEGATIVE_INFINITY) {
+ // w = (0,0) but "signed" by the direction cosines (even in they are nan).
+ u = Math.copySign(0., cosy);
+ v = Math.copySign(0., siny);
+
+ } else if (x == Double.POSITIVE_INFINITY) {
+ if (!Double.isNaN(cosy)) {
+ // w = (inf,inf), but "signed" by the direction cosines.
+ u = Math.copySign(x, cosy);
+ v = Math.copySign(x, siny);
+ } else {
+ // Provisionally w = (inf,nan), which will raise domain error if y!=nan.
+ u = x;
+ v = Double.NaN;
+ }
+
+ } else if (x > NEARLY_LN_DBL_MAX) {
+ // r = e**x would overflow but maybe not r*cos(y) and r*sin(y).
+ r = Math.exp(x - 1); // = r / e
+ u = r * cosy * Math.E;
+ v = r * siny * Math.E;
+ if (Double.isInfinite(u) || Double.isInfinite(v)) {
+ // A finite x gave rise to an infinite u or v.
+ throw math.mathRangeError();
+ }
+
+ } else {
+ // Normal case, without risk of overflow.
+ // Compute r = exp(x), and return w = u + iv = r (cos(y) + i*sin(y))
+ r = Math.exp(x);
+ u = r * cosy;
+ v = r * siny;
+ }
+ }
+ // If that generated a nan, and there wasn't one in the argument, raise domain error.
+ return exceptNaN(new PyComplex(u, v), zz);
}
public static PyComplex log(PyObject in) {
@@ -396,4 +454,46 @@
return new PyComplex(((rs * rc) + (is * ic)) / d, ((is * rc) - (rs * ic)) / d);
}
+
+ /**
+ * Turn a <code>NaN</code> result into a thrown <code>ValueError</code>, a math domain error, if
+ * the original argument was not itself <code>NaN</code>. A <code>PyComplex</code> is a
+ * <code>NaN</code> if either component is a <code>NaN</code>.
+ *
+ * @param result to return (if we return)
+ * @param arg to include in check
+ * @return result if <code>arg</code> was <code>NaN</code> or <code>result</code> was not
+ * <code>NaN</code>
+ * @throws PyException (ValueError) if <code>result</code> was <code>NaN</code> and
+ * <code>arg</code> was not <code>NaN</code>
+ */
+ private static PyComplex exceptNaN(PyComplex result, PyComplex arg) throws PyException {
+ if ((Double.isNaN(result.real) || Double.isNaN(result.imag))
+ && !(Double.isNaN(arg.real) || Double.isNaN(arg.imag))) {
+ throw math.mathDomainError();
+ } else {
+ return result;
+ }
+ }
+
+ /**
+ * Turn an infinite result into a thrown <code>OverflowError</code>, a math range error, if the
+ * original argument was not itself infinite. A <code>PyComplex</code> is infinite if either
+ * component is infinite.
+ *
+ * @param result to return (if we return)
+ * @param arg to include in check
+ * @return result if <code>arg</code> was infinite or <code>result</code> was not infinite
+ * @throws PyException (ValueError) if <code>result</code> was infinite and <code>arg</code> was
+ * not infinite
+ */
+ private static PyComplex exceptInf(PyComplex result, PyComplex arg) {
+ if ((Double.isInfinite(result.real) || Double.isInfinite(result.imag))
+ && !(Double.isInfinite(arg.real) || Double.isInfinite(arg.imag))) {
+ throw math.mathRangeError();
+ } else {
+ return result;
+ }
+ }
+
}
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
@@ -515,7 +515,7 @@
*
* @return ValueError("math domain error")
*/
- private static PyException mathDomainError() {
+ static PyException mathDomainError() {
return Py.ValueError("math domain error");
}
@@ -524,7 +524,7 @@
*
* @return OverflowError("math range error")
*/
- private static PyException mathRangeError() {
+ static PyException mathRangeError() {
return Py.OverflowError("math range error");
}
@@ -575,7 +575,7 @@
*/
private static double exceptInf(double result, double arg) {
if (Double.isInfinite(result) && !Double.isInfinite(arg)) {
- throw Py.OverflowError("math range error");
+ throw mathRangeError();
} else {
return result;
}
--
Repository URL: https://hg.python.org/jython
More information about the Jython-checkins
mailing list