@@ -694,36 +694,108 @@ public static PyComplex sqrt(PyObject w) {
694694 }
695695 }
696696
697- public static PyComplex tan (PyObject in ) {
698- PyComplex x = complexFromPyObject (in );
699-
700- double sr = Math .sin (x .real );
701- double cr = Math .cos (x .real );
702- double shi = math .sinh (x .imag );
703- double chi = math .cosh (x .imag );
704- double rs = sr * chi ;
705- double is = cr * shi ;
706- double rc = cr * chi ;
707- double ic = -sr * shi ;
708- double d = rc * rc + ic * ic ;
697+ /**
698+ * Return the tangent of z.
699+ *
700+ * @param z
701+ * @return tan <i>z</i>
702+ */
703+ public static PyComplex tan (PyObject z ) {
704+ return tanOrTanh (complexFromPyObject (z ), false );
705+ }
709706
710- return new PyComplex (((rs * rc ) + (is * ic )) / d , ((is * rc ) - (rs * ic )) / d );
707+ /**
708+ * Return the hyperbolic tangent of z.
709+ *
710+ * @param z
711+ * @return tanh <i>z</i>
712+ */
713+ public static PyComplex tanh (PyObject z ) {
714+ return tanOrTanh (complexFromPyObject (z ), true );
711715 }
712716
713- public static PyComplex tanh (PyObject in ) {
714- PyComplex x = complexFromPyObject (in );
717+ /**
718+ * Helper to compute either tan <i>z</i> or tanh <i>z</i>. The expression used is:
719+ * <p>
720+ * tanh(<i>x+iy</i>) = (sinh <i>x</i> cosh <i>x + i</i> sin <i>y</i> cos <i>y</i>) /
721+ * (sinh<sup>2</sup><i>x +</i> cos<sup>2</sup><i>y</i>)
722+ * <p>
723+ * A simplification is made for x sufficiently large that <i>e<sup>2|x|</sup></i>≫1 that
724+ * deals satisfactorily with large or infinite <i>x</i>. When computing tan, we evaluate
725+ * <i>i</i> tan <i>iz</i> instead, and the approximation applies to
726+ * <i>e<sup>2|y|</sup></i>≫1.
727+ *
728+ * @param z
729+ * @param h <code>true</code> to compute tanh <i>z</i>, <code>false</code> to compute tan
730+ * <i>z</i>.
731+ * @return tan or tanh <i>z</i>
732+ */
733+ private static PyComplex tanOrTanh (PyComplex z , boolean h ) {
734+ double x , y , u , v , s ;
735+ PyComplex w ;
715736
716- double si = Math .sin (x .imag );
717- double ci = Math .cos (x .imag );
718- double shr = math .sinh (x .real );
719- double chr = math .cosh (x .real );
720- double rs = ci * shr ;
721- double is = si * chr ;
722- double rc = ci * chr ;
723- double ic = si * shr ;
724- double d = rc * rc + ic * ic ;
725-
726- return new PyComplex (((rs * rc ) + (is * ic )) / d , ((is * rc ) - (rs * ic )) / d );
737+ if (h ) {
738+ // We compute w = tanh(z). Let w = u + iv and z = x + iy.
739+ x = z .real ;
740+ y = z .imag ;
741+ // Then the function body computes tanh(x+iy), according to:
742+ // s = sinh**2 x + cos**2 y
743+ // u = sinh x cosh x / s,
744+ // v = sin y cos y / s,
745+ // And we return w = u + iv.
746+ } else {
747+ // We compute w = tan(z). Unusually, let z = y - ix, so x + iy = iz.
748+ y = z .real ;
749+ x = -z .imag ;
750+ // Then the function body computes tanh(x+iy) = tanh(iz) = i tan(z) as before,
751+ // but we finally return w = v - iu = tan(z).
752+ }
753+
754+ if (y == 0. ) {
755+ // Real argument for tanh (or imaginary for tan).
756+ u = Math .tanh (x );
757+ // v is zero but follows the sign of y (which could be -0.0).
758+ v = y ;
759+
760+ } else if (x == 0. && !Double .isNaN (y )) {
761+ // Imaginary argument for tanh (or real for tan): imaginary result at this point.
762+ v = Math .tan (y ); // May raise domain error
763+ // u is zero but follows sign of x (which could be -0.0).
764+ u = x ;
765+
766+ } else {
767+ // The trig calls will not throw, although if y is infinite, they return nan.
768+ double cosy = Math .cos (y ), siny = Math .sin (y ), absx = Math .abs (x );
769+
770+ if (absx > ATLEAST_27LN2 ) {
771+ // e**2x is much greater than 1: exponential approximation to sinh and cosh.
772+ s = 0.25 * Math .exp (2 * absx );
773+ u = Math .copySign (1. , x );
774+ if (s == Double .POSITIVE_INFINITY ) {
775+ // Either x is inf or 2x is large enough to overflow exp(). v=0, but signed:
776+ v = Math .copySign (0. , siny * cosy );
777+ } else {
778+ v = siny * cosy / s ;
779+ }
780+
781+ } else {
782+ // Normal case: possible overflow in s near (x,y) = (0,pi/2) is harmless.
783+ double sinhx = Math .sinh (x ), coshx = Math .cosh (x );
784+ s = sinhx * sinhx + cosy * cosy ;
785+ u = sinhx * coshx / s ;
786+ v = siny * cosy / s ;
787+ }
788+ }
789+
790+ // Compose the result w according to whether we're computing tan(z) or tanh(z).
791+ if (h ) {
792+ w = new PyComplex (u , v ); // w = u + iv = tanh(x+iy).
793+ } else {
794+ w = new PyComplex (v , -u ); // w = v - iu = tan(y-ix) = tan(z)
795+ }
796+
797+ // If that generated a nan, and there wasn't one in the argument, raise a domain error.
798+ return exceptNaN (w , z );
727799 }
728800
729801 /**
0 commit comments