Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e574e29

Browse files
committed
ENH: Import the ccosh/ccos implementation from FreeBSD
The code from FreeBSD was lightly adapted to fit with the numpy style and to correct an Annex G failure. (ccosh(Inf + 0j) should be (Inf + 0), and symmetric). With this commit, npy_ccosh(f) and npy_ccos(f) pass all of the tests in test_c99complex.c.
1 parent 77705ab commit e574e29

1 file changed

Lines changed: 131 additions & 11 deletions

File tree

numpy/core/src/npymath/npy_math_complex.c.src

Lines changed: 131 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)