22
33import org .python .core .Py ;
44import org .python .core .PyComplex ;
5+ import org .python .core .PyException ;
56import org .python .core .PyFloat ;
67import org .python .core .PyInstance ;
78import org .python .core .PyObject ;
@@ -19,6 +20,8 @@ public class cmath {
1920
2021 /** 2<sup>-½</sup> (Ref: Abramowitz & Stegun [1972], p2). */
2122 private static final double ROOT_HALF = 0.70710678118654752440 ;
23+ /** ln({@link Double#MAX_VALUE}) or a little less */
24+ private static final double NEARLY_LN_DBL_MAX = 709.4361393 ;
2225
2326 private static PyComplex c_prodi (PyComplex x ) {
2427 return (PyComplex )x .__mul__ (i );
@@ -140,10 +143,65 @@ public static PyComplex cosh(PyObject in) {
140143 * math .sinh (x .real ));
141144 }
142145
143- public static PyComplex exp (PyObject in ) {
144- PyComplex x = complexFromPyObject (in );
145- double l = Math .exp (x .real );
146- return new PyComplex (l * Math .cos (x .imag ), l * Math .sin (x .imag ));
146+ /**
147+ * Return the exponential value e<sup>z</sup>.
148+ *
149+ * @param z
150+ * @return e<sup>z</sup>
151+ */
152+ public static PyComplex exp (PyObject z ) {
153+ PyComplex zz = complexFromPyObject (z );
154+ double x = zz .real , y = zz .imag , r , u , v ;
155+ /*
156+ * This has a lot of corner-cases, and some of them make little sense sense, but it matches
157+ * CPython and passes the regression tests.
158+ */
159+ if (y == 0. ) {
160+ // Real value: use a real solution. (This may raise a range error.)
161+ u = math .exp (x );
162+ // v follows sign of y.
163+ v = y ;
164+
165+ } else {
166+ // The trig calls will not throw, although if y is infinite, they return nan.
167+ double cosy = Math .cos (y ), siny = Math .sin (y );
168+
169+ if (x == Double .NEGATIVE_INFINITY ) {
170+ // w = (0,0) but "signed" by the direction cosines (even in they are nan).
171+ u = Math .copySign (0. , cosy );
172+ v = Math .copySign (0. , siny );
173+
174+ } else if (x == Double .POSITIVE_INFINITY ) {
175+ if (!Double .isNaN (cosy )) {
176+ // w = (inf,inf), but "signed" by the direction cosines.
177+ u = Math .copySign (x , cosy );
178+ v = Math .copySign (x , siny );
179+ } else {
180+ // Provisionally w = (inf,nan), which will raise domain error if y!=nan.
181+ u = x ;
182+ v = Double .NaN ;
183+ }
184+
185+ } else if (x > NEARLY_LN_DBL_MAX ) {
186+ // r = e**x would overflow but maybe not r*cos(y) and r*sin(y).
187+ r = Math .exp (x - 1 ); // = r / e
188+ u = r * cosy * Math .E ;
189+ v = r * siny * Math .E ;
190+ if (Double .isInfinite (u ) || Double .isInfinite (v )) {
191+ // A finite x gave rise to an infinite u or v.
192+ throw math .mathRangeError ();
193+ }
194+
195+ } else {
196+ // Normal case, without risk of overflow.
197+ // Compute r = exp(x), and return w = u + iv = r (cos(y) + i*sin(y))
198+ r = Math .exp (x );
199+ u = r * cosy ;
200+ v = r * siny ;
201+ }
202+ }
203+ // If that generated a nan, and there wasn't one in the argument, raise domain error.
204+ return exceptNaN (new PyComplex (u , v ), zz );
147205 }
148206
149207 public static PyComplex log (PyObject in ) {
@@ -396,4 +454,46 @@ public static PyComplex tanh(PyObject in) {
396454
397455 return new PyComplex (((rs * rc ) + (is * ic )) / d , ((is * rc ) - (rs * ic )) / d );
398456 }
457+
458+ /**
459+ * Turn a <code>NaN</code> result into a thrown <code>ValueError</code>, a math domain error, if
460+ * the original argument was not itself <code>NaN</code>. A <code>PyComplex</code> is a
461+ * <code>NaN</code> if either component is a <code>NaN</code>.
462+ *
463+ * @param result to return (if we return)
464+ * @param arg to include in check
465+ * @return result if <code>arg</code> was <code>NaN</code> or <code>result</code> was not
466+ * <code>NaN</code>
467+ * @throws PyException (ValueError) if <code>result</code> was <code>NaN</code> and
468+ * <code>arg</code> was not <code>NaN</code>
469+ */
470+ private static PyComplex exceptNaN (PyComplex result , PyComplex arg ) throws PyException {
471+ if ((Double .isNaN (result .real ) || Double .isNaN (result .imag ))
472+ && !(Double .isNaN (arg .real ) || Double .isNaN (arg .imag ))) {
473+ throw math .mathDomainError ();
474+ } else {
475+ return result ;
476+ }
477+ }
478+
479+ /**
480+ * Turn an infinite result into a thrown <code>OverflowError</code>, a math range error, if the
481+ * original argument was not itself infinite. A <code>PyComplex</code> is infinite if either
482+ * component is infinite.
483+ *
484+ * @param result to return (if we return)
485+ * @param arg to include in check
486+ * @return result if <code>arg</code> was infinite or <code>result</code> was not infinite
487+ * @throws PyException (ValueError) if <code>result</code> was infinite and <code>arg</code> was
488+ * not infinite
489+ */
490+ private static PyComplex exceptInf (PyComplex result , PyComplex arg ) {
491+ if ((Double .isInfinite (result .real ) || Double .isInfinite (result .imag ))
492+ && !(Double .isInfinite (arg .real ) || Double .isInfinite (arg .imag ))) {
493+ throw math .mathRangeError ();
494+ } else {
495+ return result ;
496+ }
497+ }
498+
399499}
0 commit comments