@@ -22,6 +22,14 @@ public class cmath {
2222 private static final double ROOT_HALF = 0.70710678118654752440 ;
2323 /** ln({@link Double#MAX_VALUE}) or a little less */
2424 private static final double NEARLY_LN_DBL_MAX = 709.4361393 ;
25+ /**
26+ * For x larger than this, <i>e<sup>-x</sup></i> is negligible compared with
27+ * <i>e<sup>x</sup></i>, or equivalently 1 is negligible compared with <i>e<sup>2x</sup></i>, in
28+ * IEEE-754 floating point. Beyond this, sinh <i>x</i> and cosh <i>x</i> are adequately
29+ * approximated by 0.5<i>e<sup>x</sup></i>. The smallest theoretical value is 27 ln(2).
30+ */
31+ private static final double ATLEAST_27LN2 = 18.72 ;
32+ private static final double HALF_E2 = 0.5 * Math .E * Math .E ;
2533
2634 /** log<sub>10</sub>e (Ref: Abramowitz & Stegun [1972], p3). */
2735 private static final double LOG10E = 0.43429448190325182765 ;
@@ -381,16 +389,110 @@ private static double logHypot(double u, double v) {
381389 }
382390 }
383391
384- public static PyComplex sin (PyObject in ) {
385- PyComplex x = complexFromPyObject (in );
386- return new PyComplex (Math .sin (x .real ) * math .cosh (x .imag ), Math .cos (x .real )
387- * math .sinh (x .imag ));
392+ /**
393+ * Return the sine of z.
394+ * @param z
395+ * @return sin <i>z</i>
396+ */
397+ public static PyComplex sin (PyObject z ) {
398+ return sinOrSinh (complexFromPyObject (z ), false );
388399 }
389400
390- public static PyComplex sinh (PyObject in ) {
391- PyComplex x = complexFromPyObject (in );
392- return new PyComplex (Math .cos (x .imag ) * math .sinh (x .real ), Math .sin (x .imag )
393- * math .cosh (x .real ));
401+ /**
402+ * Return the hyperbolic sine of z.
403+ * @param z
404+ * @return sinh <i>z</i>
405+ */
406+ public static PyComplex sinh (PyObject z ) {
407+ return sinOrSinh (complexFromPyObject (z ), true );
408+ }
409+
410+ /**
411+ * Helper to compute either sin <i>z</i> or sinh <i>z</i>.
412+ *
413+ * @param z
414+ * @param h <code>true</code> to compute sinh <i>z</i>, <code>false</code> to compute sin
415+ * <i>z</i>.
416+ * @return
417+ */
418+ private static PyComplex sinOrSinh (PyComplex z , boolean h ) {
419+ double x , y , u , v ;
420+ PyComplex w ;
421+
422+ if (h ) {
423+ // We compute w = sinh(z). Let w = u + iv and z = x + iy. We compute w = sinh(z) from:
424+ x = z .real ;
425+ y = z .imag ;
426+ // Then the function body computes sinh(x+iy), according to:
427+ // u = sinh(x) cos(y),
428+ // v = cosh(x) sin(y),
429+ // And we return w = u + iv.
430+ } else {
431+ // We compute w = sin(z). Unusually, let z = y - ix.
432+ y = z .real ;
433+ x = -z .imag ;
434+ // Then the function body computes sinh(x+iy) = sinh(iz) = i sin(z), according to:
435+ // u = sinh(x) cos(y),
436+ // v = cosh(x) sin(y).
437+ // But we finally return w = v - iu = sin(z)
438+ }
439+
440+ if (y == 0. ) {
441+ // Real argument for sinh (or imaginary for sin): use real library.
442+ u = math .sinh (x ); // This will raise a range error on overflow.
443+ // v follows the sign of y (which could be -0.0).
444+ v = y ;
445+
446+ } else if (x == 0. ) {
447+ // Imaginary argument for sinh (or real for sin): imaginary result at this point.
448+ v = Math .sin (y );
449+ // u follows sign of x (which could be -0.0).
450+ u = x ;
451+
452+ } else {
453+
454+ // The trig calls will not throw, although if y is infinite, they return nan.
455+ double cosy = Math .cos (y ), siny = Math .sin (y ), absx = Math .abs (x );
456+
457+ if (absx == Double .POSITIVE_INFINITY ) {
458+ if (!Double .isNaN (cosy )) {
459+ // w = (inf,inf), but "rotated" by the direction cosines.
460+ u = x * cosy ;
461+ v = Math .abs (x ) * siny ;
462+ } else {
463+ // Provisionally w = (inf,nan), which will raise domain error if y!=nan.
464+ u = x ;
465+ v = Double .NaN ;
466+ }
467+
468+ } else if (absx > ATLEAST_27LN2 ) {
469+ // Use 0.5*e**x approximation. This is also the region where we risk overflow.
470+ double r = Math .exp (absx - 2. );
471+ // r approximates 2cosh(x)/e**2: multiply in this order to avoid inf:
472+ v = r * siny * HALF_E2 ;
473+ // r approximates 2sinh(|x|)/e**2: put back the proper sign of x in passing.
474+ u = Math .copySign (r , x ) * cosy * HALF_E2 ;
475+ if (Double .isInfinite (u ) || Double .isInfinite (v )) {
476+ // A finite x gave rise to an infinite u or v.
477+ throw math .mathRangeError ();
478+ }
479+
480+ } else {
481+ // Normal case, without risk of overflow.
482+ u = Math .sinh (x ) * cosy ;
483+ v = Math .cosh (x ) * siny ;
484+ }
485+ }
486+
487+ // Compose the result w according to whether we're computing sin(z) or sinh(z).
488+ if (h ) {
489+ w = new PyComplex (u , v ); // w = u + iv = sinh(x+iy).
490+ } else {
491+ w = new PyComplex (v , -u ); // w = v - iu = sin(y-ix) = sin(z)
492+ }
493+
494+ // If that generated a nan, and there wasn't one in the argument, raise a domain error.
495+ return exceptNaN (w , z );
394496 }
395497
396498 /**
0 commit comments