@@ -157,16 +157,20 @@ static NPY_INLINE @ctype@ cmuli@c@(@ctype@ a)
157157 *
158158 * This is based on the technique used in FreeBSD's __frexp_exp and
159159 * __ldexp_(c)exp functions by David Schultz.
160+ *
161+ * SCALED_CEXP_LOWER = log(FLT_MAX)
162+ * SCALED_CEXP_UPPER = log(2) + log(FLT_MAX) - log(FLT_TRUE_MIN),
163+ * where FLT_TRUE_MIN is the smallest possible subnormal number.
160164 */
161165
162166#define SCALED_CEXP_LOWERF 88.722839f
163- #define SCALED_CEXP_UPPERF 192.69492
164- #define SCALED_CEXP_LOWER 710.47586007394386
167+ #define SCALED_CEXP_UPPERF 192.69492f
168+ #define SCALED_CEXP_LOWER 710.47586007394386
165169#define SCALED_CEXP_UPPER 1454.9159319953251
166170#define SCALED_CEXP_LOWERL 11357.216553474703895L
167171#define SCALED_CEXP_UPPERL 22756.021937783004509L
168172
169- static @ctype @ _npy_scaled_cexp @c @(@type @ x , @type @ y , int expt )
173+ static @ctype @ _npy_scaled_cexp @c @(@type @ x , @type @ y , npy_int expt )
170174{
171175#if @precision @ == 1
172176 const npy_int k = 235 ;
@@ -291,7 +295,7 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, int expt)
291295 @type @ ax = npy_fabs @c @(npy_creal @c @(z ));
292296 @type @ ay = npy_fabs @c @(npy_cimag @c @(z ));
293297 @type @ rr , ri ;
294-
298+
295299 if (ax > @TMAX @/4 || ay > @TMAX @/4 ) {
296300 rr = npy_log @c @(npy_hypot @c @(ax /2 , ay /2 )) + NPY_LOGE2 @c @;
297301 }
@@ -470,10 +474,8 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, int expt)
470474#ifndef HAVE_CCOS @C @
471475@ctype @ npy_ccos @c @(@ctype @ z )
472476{
473- @type @ x , y ;
474- x = npy_creal @c @(z );
475- y = npy_cimag @c @(z );
476- return npy_cpack @c @(npy_cos @c @(x ) * npy_cosh @c @(y ), - (npy_sin @c @(x ) * npy_sinh @c @(y )));
477+ /* ccos(z) = ccosh(I * z) */
478+ return npy_ccosh @c @(npy_cpack @c @(- npy_cimag @c @(z ), npy_creal @c @(z )));
477479}
478480#endif
479481
@@ -493,17 +495,135 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, int expt)
493495 /* ctan(z) = -I * ctanh(I * z) */
494496 z = npy_ctanh @c @(npy_cpack @c @(- npy_cimag @c @(z ), npy_creal @c @(z )));
495497 return (npy_cpack @c @(npy_cimag @c @(z ), - npy_creal @c @(z )));
496- }
498+ }
497499#endif
498500
499501#ifndef HAVE_CCOSH @C @
502+ /*
503+ * Taken from the msun library in FreeBSD, rev 226599.
504+ *
505+ * Hyperbolic cosine of a complex argument z = x + i y.
506+ *
507+ * cosh(z) = cosh(x+iy)
508+ * = cosh(x) cos(y) + i sinh(x) sin(y).
509+ *
510+ * Exceptional values are noted in the comments within the source code.
511+ * These values and the return value were taken from n1124.pdf.
512+ *
513+ * CCOSH_BIG is chosen such that
514+ * spacing(0.5 * exp(CCOSH_BIG)) > 0.5*exp(-CCOSH_BIG)
515+ * although the exact value assigned to CCOSH_BIG is not so important
516+ */
517+ #if @precision @ == 1
518+ #define CCOSH_BIG 9.0f
519+ #define CCOSH_HUGE 1.70141183e+38f
520+ #endif
521+ #if @precision @ == 2
522+ #define CCOSH_BIG 22.0
523+ #define CCOSH_HUGE 8.9884656743115795e+307
524+ #endif
525+ #if @precision @ >= 3
526+ #define CCOSH_BIG 24.0L
527+ #define CCOSH_HUGE 5.94865747678615882543e+4931L
528+ #endif
529+
500530@ctype @ npy_ccosh @c @(@ctype @ z )
501531{
502- @type @ x , y ;
532+ @type @ x , y , h , absx ;
533+ npy_int xfinite , yfinite ;
534+
503535 x = npy_creal @c @(z );
504536 y = npy_cimag @c @(z );
505- return npy_cpack @c @(npy_cos @c @(y )* npy_cosh @c @(x ), npy_sin @c @(y )* npy_sinh @c @(x ));
537+
538+ xfinite = npy_isfinite (x );
539+ yfinite = npy_isfinite (y );
540+
541+ /* Handle the nearly-non-exceptional cases where x and y are finite. */
542+ if (xfinite && yfinite ) {
543+ if (y == 0 )
544+ return npy_cpack @c @(npy_cosh @c @(x ), x * y );
545+ absx = npy_fabs @c @(x );
546+ if (absx < CCOSH_BIG ) /* small x: normal case */
547+ return npy_cpack @c @(npy_cosh @c @(x ) * npy_cos @c @(y ), npy_sinh @c @(x ) * npy_sin @c @(y ));
548+
549+ /* |x| >= 22, so cosh(x) ~= exp(|x|) */
550+ if (absx < SCALED_CEXP_LOWER @C @) {
551+ /* x < 710: exp(|x|) won't overflow */
552+ h = npy_exp @c @(absx ) * 0.5 ;
553+ return npy_cpack @c @(h * npy_cos @c @(y ), npy_copysign @c @(h , x ) * npy_sin @c @(y ));
554+ } else if (absx < SCALED_CEXP_UPPER @C @) {
555+ /* x < 1455: scale to avoid overflow */
556+ z = _npy_scaled_cexp @c @(absx , y , -1 );
557+ return npy_cpack @c @(npy_creal @c @(z ), npy_cimag @c @(z ) * npy_copysign @c @(1 , x ));
558+ } else {
559+ /* x >= 1455: the result always overflows */
560+ h = CCOSH_HUGE * x ;
561+ return npy_cpack @c @(h * h * npy_cos @c @(y ), h * npy_sin @c @(y ));
562+ }
563+ }
564+
565+ /*
566+ * cosh(+-0 +- I Inf) = dNaN + I sign(d(+-0, dNaN))0.
567+ * The sign of 0 in the result is unspecified. Choice = normally
568+ * the same as dNaN. Raise the invalid floating-point exception.
569+ *
570+ * cosh(+-0 +- I NaN) = d(NaN) + I sign(d(+-0, NaN))0.
571+ * The sign of 0 in the result is unspecified. Choice = normally
572+ * the same as d(NaN).
573+ */
574+ if (x == 0 && !yfinite )
575+ return npy_cpack @c @(y - y , npy_copysign @c @(0 , x * (y - y )));
576+
577+ /*
578+ * cosh(+-Inf +- I 0) = +Inf + I (+-)(+-)0.
579+ *
580+ * cosh(NaN +- I 0) = d(NaN) + I sign(d(NaN, +-0))0.
581+ * The sign of 0 in the result is unspecified.
582+ */
583+ if (y == 0 && !xfinite )
584+ return npy_cpack @c @(x * x , npy_copysign @c @(0 , x ) * y );
585+
586+ /*
587+ * cosh(x +- I Inf) = dNaN + I dNaN.
588+ * Raise the invalid floating-point exception for finite nonzero x.
589+ *
590+ * cosh(x + I NaN) = d(NaN) + I d(NaN).
591+ * Optionally raises the invalid floating-point exception for finite
592+ * nonzero x. Choice = don't raise (except for signaling NaNs).
593+ */
594+ if (xfinite && !yfinite )
595+ return npy_cpack @c @(y - y , x * (y - y ));
596+
597+ /*
598+ * cosh(+-Inf + I NaN) = +Inf + I d(NaN).
599+ *
600+ * cosh(+-Inf +- I Inf) = +Inf + I dNaN.
601+ * The sign of Inf in the result is unspecified. Choice = always +.
602+ * Raise the invalid floating-point exception.
603+ *
604+ * cosh(+-Inf + I y) = +Inf cos(y) +- I Inf sin(y)
605+ */
606+ if (npy_isinf (x )) {
607+ if (!yfinite )
608+ return npy_cpack @c @(x * x , x * (y - y ));
609+ return npy_cpack @c @((x * x ) * npy_cos @c @(y ), x * npy_sin @c @(y ));
610+ }
611+
612+ /*
613+ * cosh(NaN + I NaN) = d(NaN) + I d(NaN).
614+ *
615+ * cosh(NaN +- I Inf) = d(NaN) + I d(NaN).
616+ * Optionally raises the invalid floating-point exception.
617+ * Choice = raise.
618+ *
619+ * cosh(NaN + I y) = d(NaN) + I d(NaN).
620+ * Optionally raises the invalid floating-point exception for finite
621+ * nonzero y. Choice = don't raise (except for signaling NaNs).
622+ */
623+ return npy_cpack @c @((x * x ) * (y - y ), (x + x ) * (y - y ));
506624}
625+ #undef CCOSH_BIG
626+ #undef CCOSH_HUGE
507627#endif
508628
509629#ifndef HAVE_CSINH @C @
0 commit comments