From 9e7be8cfe8f8e734ed3af3cbd50d9caa2ad45dc9 Mon Sep 17 00:00:00 2001 From: Eric Moore Date: Fri, 22 Feb 2013 17:30:52 -0500 Subject: [PATCH 01/35] BUG: Overflow in tan and tanh for large complex values gh-2321 (trac 1726). np.tanh(1000+0j) gave nan+nan*j, should be 1.0+0j. The same bug was present in np.tan(0+1000j). Bug fixed by replacing our complex tan and tanh implementation with one from FreeBSD. --- numpy/core/include/numpy/npy_math.h | 9 ++ numpy/core/setup_common.py | 2 +- numpy/core/src/npymath/npy_math_complex.c.src | 123 +++++++++++++++++- numpy/core/src/umath/funcs.inc.src | 34 +---- 4 files changed, 133 insertions(+), 35 deletions(-) diff --git a/numpy/core/include/numpy/npy_math.h b/numpy/core/include/numpy/npy_math.h index b7920460d88a..b1151949796e 100644 --- a/numpy/core/include/numpy/npy_math.h +++ b/numpy/core/include/numpy/npy_math.h @@ -420,6 +420,9 @@ npy_cdouble npy_csqrt(npy_cdouble z); npy_cdouble npy_ccos(npy_cdouble z); npy_cdouble npy_csin(npy_cdouble z); +npy_cdouble npy_ctan(npy_cdouble z); + +npy_cdouble npy_ctanh(npy_cdouble z); /* * Single precision complex functions @@ -435,6 +438,9 @@ npy_cfloat npy_csqrtf(npy_cfloat z); npy_cfloat npy_ccosf(npy_cfloat z); npy_cfloat npy_csinf(npy_cfloat z); +npy_cfloat npy_ctanf(npy_cfloat z); + +npy_cfloat npy_ctanhf(npy_cfloat z); /* * Extended precision complex functions @@ -450,6 +456,9 @@ npy_clongdouble npy_csqrtl(npy_clongdouble z); npy_clongdouble npy_ccosl(npy_clongdouble z); npy_clongdouble npy_csinl(npy_clongdouble z); +npy_clongdouble npy_ctanl(npy_clongdouble z); + +npy_clongdouble npy_ctanhl(npy_clongdouble z); /* * Functions that set the floating point error diff --git a/numpy/core/setup_common.py b/numpy/core/setup_common.py index 2db4b4331ed6..c10c322d8387 100644 --- a/numpy/core/setup_common.py +++ b/numpy/core/setup_common.py @@ -144,7 +144,7 @@ def check_api_version(apiversion, codegen_dir): C99_COMPLEX_TYPES = ['complex double', 'complex float', 'complex long double'] C99_COMPLEX_FUNCS = ['creal', 'cimag', 'cabs', 'carg', 'cexp', 'csqrt', 'clog', - 'ccos', 'csin', 'cpow'] + 'ccos', 'csin', 'cpow', 'ctan', 'ctanh'] def fname2def(name): return "HAVE_%s" % name.upper() diff --git a/numpy/core/src/npymath/npy_math_complex.c.src b/numpy/core/src/npymath/npy_math_complex.c.src index 920f107b89b2..2ebe686a2ddc 100644 --- a/numpy/core/src/npymath/npy_math_complex.c.src +++ b/numpy/core/src/npymath/npy_math_complex.c.src @@ -4,7 +4,7 @@ * Most of the code is taken from the msun library in FreeBSD (HEAD @ 30th June * 2009), under the following license: * - * Copyright (c) 2007 David Schultz + * Copyright (c) 2007, 2011 David Schultz * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -227,6 +227,123 @@ return npy_cpack@c@(npy_sin@c@(x) * npy_cosh@c@(y), npy_cos@c@(x) * npy_sinh@c@(y)); } #endif + +#ifndef HAVE_CTAN@C@ +@ctype@ npy_ctan@c@(@ctype@ z) +{ + /* ctan(z) = -I * ctanh(I * z) */ + z = npy_ctanh@c@(npy_cpack@c@(-npy_cimag@c@(z), npy_creal@c@(z))); + return (npy_cpack@c@(npy_cimag@c@(z), -npy_creal@c@(z))); +} +#endif + +#ifndef HAVE_CTANH@C@ +/* + * Taken from the msun library in FreeBSD, rev 226600. + * + * Hyperbolic tangent of a complex argument z = x + i y. + * + * The algorithm is from: + * + * W. Kahan. Branch Cuts for Complex Elementary Functions or Much + * Ado About Nothing's Sign Bit. In The State of the Art in + * Numerical Analysis, pp. 165 ff. Iserles and Powell, eds., 1987. + * + * Method: + * + * Let t = tan(x) + * beta = 1/cos^2(y) + * s = sinh(x) + * rho = cosh(x) + * + * We have: + * + * tanh(z) = sinh(z) / cosh(z) + * + * sinh(x) cos(y) + i cosh(x) sin(y) + * = --------------------------------- + * cosh(x) cos(y) + i sinh(x) sin(y) + * + * cosh(x) sinh(x) / cos^2(y) + i tan(y) + * = ------------------------------------- + * 1 + sinh^2(x) / cos^2(y) + * + * beta rho s + i t + * = ---------------- + * 1 + beta s^2 + * + * Modifications: + * + * I omitted the original algorithm's handling of overflow in tan(x) after + * verifying with nearpi.c that this can't happen in IEEE single or double + * precision. I also handle large x differently. + */ + +#define TANH_HUGE 22.0 +#define TANHF_HUGE 11.0F +#define TANHL_HUGE 42.0L + +@ctype@ npy_ctanh@c@(@ctype@ z) +{ + @type@ x, y; + @type@ t, beta, s, rho, denom; + + x = npy_creal@c@(z); + y = npy_cimag@c@(z); + + /* + * ctanh(NaN + i 0) = NaN + i 0 + * + * ctanh(NaN + i y) = NaN + i NaN for y != 0 + * + * The imaginary part has the sign of x*sin(2*y), but there's no + * special effort to get this right. + * + * ctanh(+-Inf +- i Inf) = +-1 +- 0 + * + * ctanh(+-Inf + i y) = +-1 + 0 sin(2y) for y finite + * + * The imaginary part of the sign is unspecified. This special + * case is only needed to avoid a spurious invalid exception when + * y is infinite. + */ + if (!npy_isfinite(x)) { + if (npy_isnan(x)) + return npy_cpack@c@(x, (y == 0 ? y : x * y)); + return npy_cpack@c@(npy_copysign@c@(1,x), + npy_copysign@c@(0, npy_isinf(y) ? y : npy_sin@c@(y) * npy_cos@c@(y))); + } + + /* + * ctanh(x + i NAN) = NaN + i NaN + * ctanh(x +- i Inf) = NaN + i NaN + */ + if (!npy_isfinite(y)) + return (npy_cpack@c@(y - y, y - y)); + + /* + * ctanh(+-huge + i +-y) ~= +-1 +- i 2sin(2y)/exp(2x), using the + * approximation sinh^2(huge) ~= exp(2*huge) / 4. + * We use a modified formula to avoid spurious overflow. + */ + if (x >= TANH@C@_HUGE) { + @type@ exp_mx = npy_exp@c@(-npy_fabs@c@(x)); + return (npy_cpack@c@(npy_copysign@c@(1, x), + 4 * npy_sin@c@(y) * npy_cos@c@(y) * exp_mx * exp_mx)); + } + + /* Kahan's algorithm */ + t = npy_tan@c@(y); + beta = 1.0 + t * t; /* = 1 / cos^2(y) */ + s = npy_sinh@c@(x); + rho = npy_sqrt@c@(1 + s * s); /* = cosh(x) */ + denom = 1 + beta * s * s; + return (npy_cpack@c@((beta * rho * s) / denom, t / denom)); +} +#undef TANH_HUGE +#undef TANHF_HUGE +#undef TANHL_HUGE +#endif /**end repeat**/ /*========================================================== @@ -254,8 +371,8 @@ /**end repeat1**/ /**begin repeat1 - * #kind = cexp,clog,csqrt,ccos,csin# - * #KIND = CEXP,CLOG,CSQRT,CCOS,CSIN# + * #kind = cexp,clog,csqrt,ccos,csin,ctan,ctanh# + * #KIND = CEXP,CLOG,CSQRT,CCOS,CSIN,CTAN,CTANH# */ #ifdef HAVE_@KIND@@C@ @ctype@ npy_@kind@@c@(@ctype@ z) diff --git a/numpy/core/src/umath/funcs.inc.src b/numpy/core/src/umath/funcs.inc.src index 9df39e41f0d0..15c6620e6184 100644 --- a/numpy/core/src/umath/funcs.inc.src +++ b/numpy/core/src/umath/funcs.inc.src @@ -671,42 +671,14 @@ nc_sinh@c@(@ctype@ *x, @ctype@ *r) static void nc_tan@c@(@ctype@ *x, @ctype@ *r) { - @ftype@ sr,cr,shi,chi; - @ftype@ rs,is,rc,ic; - @ftype@ d; - @ftype@ xr=x->real, xi=x->imag; - sr = npy_sin@c@(xr); - cr = npy_cos@c@(xr); - shi = npy_sinh@c@(xi); - chi = npy_cosh@c@(xi); - rs = sr*chi; - is = cr*shi; - rc = cr*chi; - ic = -sr*shi; - d = rc*rc + ic*ic; - r->real = (rs*rc+is*ic)/d; - r->imag = (is*rc-rs*ic)/d; - return; + *r = npy_ctan@c@(*x); + return; } static void nc_tanh@c@(@ctype@ *x, @ctype@ *r) { - @ftype@ si,ci,shr,chr; - @ftype@ rs,is,rc,ic; - @ftype@ d; - @ftype@ xr=x->real, xi=x->imag; - si = npy_sin@c@(xi); - ci = npy_cos@c@(xi); - shr = npy_sinh@c@(xr); - chr = npy_cosh@c@(xr); - rs = ci*shr; - is = si*chr; - rc = ci*chr; - ic = si*shr; - d = rc*rc + ic*ic; - r->real = (rs*rc+is*ic)/d; - r->imag = (is*rc-rs*ic)/d; + *r = npy_ctanh@c@(*x); return; } From f442eadcfdc84cfb8cc05eb30cc7866f501b5f7b Mon Sep 17 00:00:00 2001 From: Eric Moore Date: Fri, 1 Mar 2013 14:42:23 -0500 Subject: [PATCH 02/35] STY: npymath: tabs -> spaces, long lines --- numpy/core/src/npymath/npy_math_complex.c.src | 103 +++++++++--------- 1 file changed, 52 insertions(+), 51 deletions(-) diff --git a/numpy/core/src/npymath/npy_math_complex.c.src b/numpy/core/src/npymath/npy_math_complex.c.src index 2ebe686a2ddc..c2ad5b067e65 100644 --- a/numpy/core/src/npymath/npy_math_complex.c.src +++ b/numpy/core/src/npymath/npy_math_complex.c.src @@ -231,9 +231,9 @@ #ifndef HAVE_CTAN@C@ @ctype@ npy_ctan@c@(@ctype@ z) { - /* ctan(z) = -I * ctanh(I * z) */ - z = npy_ctanh@c@(npy_cpack@c@(-npy_cimag@c@(z), npy_creal@c@(z))); - return (npy_cpack@c@(npy_cimag@c@(z), -npy_creal@c@(z))); + /* ctan(z) = -I * ctanh(I * z) */ + z = npy_ctanh@c@(npy_cpack@c@(-npy_cimag@c@(z), npy_creal@c@(z))); + return (npy_cpack@c@(npy_cimag@c@(z), -npy_creal@c@(z))); } #endif @@ -285,60 +285,61 @@ @ctype@ npy_ctanh@c@(@ctype@ z) { - @type@ x, y; - @type@ t, beta, s, rho, denom; - - x = npy_creal@c@(z); - y = npy_cimag@c@(z); - - /* - * ctanh(NaN + i 0) = NaN + i 0 - * - * ctanh(NaN + i y) = NaN + i NaN for y != 0 - * - * The imaginary part has the sign of x*sin(2*y), but there's no - * special effort to get this right. - * - * ctanh(+-Inf +- i Inf) = +-1 +- 0 - * - * ctanh(+-Inf + i y) = +-1 + 0 sin(2y) for y finite - * - * The imaginary part of the sign is unspecified. This special - * case is only needed to avoid a spurious invalid exception when - * y is infinite. - */ + @type@ x, y; + @type@ t, beta, s, rho, denom; + + x = npy_creal@c@(z); + y = npy_cimag@c@(z); + + /* + * ctanh(NaN + i 0) = NaN + i 0 + * + * ctanh(NaN + i y) = NaN + i NaN for y != 0 + * + * The imaginary part has the sign of x*sin(2*y), but there's no + * special effort to get this right. + * + * ctanh(+-Inf +- i Inf) = +-1 +- 0 + * + * ctanh(+-Inf + i y) = +-1 + 0 sin(2y) for y finite + * + * The imaginary part of the sign is unspecified. This special + * case is only needed to avoid a spurious invalid exception when + * y is infinite. + */ if (!npy_isfinite(x)) { if (npy_isnan(x)) return npy_cpack@c@(x, (y == 0 ? y : x * y)); return npy_cpack@c@(npy_copysign@c@(1,x), - npy_copysign@c@(0, npy_isinf(y) ? y : npy_sin@c@(y) * npy_cos@c@(y))); + npy_copysign@c@(0, + npy_isinf(y) ? y : npy_sin@c@(y) * npy_cos@c@(y))); } - /* - * ctanh(x + i NAN) = NaN + i NaN - * ctanh(x +- i Inf) = NaN + i NaN - */ - if (!npy_isfinite(y)) - return (npy_cpack@c@(y - y, y - y)); - - /* - * ctanh(+-huge + i +-y) ~= +-1 +- i 2sin(2y)/exp(2x), using the - * approximation sinh^2(huge) ~= exp(2*huge) / 4. - * We use a modified formula to avoid spurious overflow. - */ - if (x >= TANH@C@_HUGE) { - @type@ exp_mx = npy_exp@c@(-npy_fabs@c@(x)); - return (npy_cpack@c@(npy_copysign@c@(1, x), - 4 * npy_sin@c@(y) * npy_cos@c@(y) * exp_mx * exp_mx)); - } - - /* Kahan's algorithm */ - t = npy_tan@c@(y); - beta = 1.0 + t * t; /* = 1 / cos^2(y) */ - s = npy_sinh@c@(x); - rho = npy_sqrt@c@(1 + s * s); /* = cosh(x) */ - denom = 1 + beta * s * s; - return (npy_cpack@c@((beta * rho * s) / denom, t / denom)); + /* + * ctanh(x + i NAN) = NaN + i NaN + * ctanh(x +- i Inf) = NaN + i NaN + */ + if (!npy_isfinite(y)) + return (npy_cpack@c@(y - y, y - y)); + + /* + * ctanh(+-huge + i +-y) ~= +-1 +- i 2sin(2y)/exp(2x), using the + * approximation sinh^2(huge) ~= exp(2*huge) / 4. + * We use a modified formula to avoid spurious overflow. + */ + if (x >= TANH@C@_HUGE) { + @type@ exp_mx = npy_exp@c@(-npy_fabs@c@(x)); + return (npy_cpack@c@(npy_copysign@c@(1, x), + 4 * npy_sin@c@(y) * npy_cos@c@(y) * exp_mx * exp_mx)); + } + + /* Kahan's algorithm */ + t = npy_tan@c@(y); + beta = 1.0 + t * t; /* = 1 / cos^2(y) */ + s = npy_sinh@c@(x); + rho = npy_sqrt@c@(1 + s * s); /* = cosh(x) */ + denom = 1 + beta * s * s; + return (npy_cpack@c@((beta * rho * s) / denom, t / denom)); } #undef TANH_HUGE #undef TANHF_HUGE From d62c59080245e1ec3695bf313b21687e3de30471 Mon Sep 17 00:00:00 2001 From: Eric Moore Date: Wed, 6 Mar 2013 16:33:53 -0500 Subject: [PATCH 03/35] ENH: Check for, and use all C99 complex functions if available Previously, a number of functions operating on complex numbers from C99 were checked for during the configuration stage. If they existed, they were used. This commit extends this to all of the C99 complex functions. All of our local implementations are now in npymath, instead of being spread between npymath and umath/funcs.inc.src. Functions that numpy has but C99 does not are still in funcs.inc.src. --- numpy/core/include/numpy/npy_math.h | 34 +- numpy/core/setup_common.py | 5 +- numpy/core/src/npymath/npy_math_complex.c.src | 359 +++++++++++++++++- numpy/core/src/umath/funcs.inc.src | 324 +--------------- 4 files changed, 391 insertions(+), 331 deletions(-) diff --git a/numpy/core/include/numpy/npy_math.h b/numpy/core/include/numpy/npy_math.h index b1151949796e..f91e17c7cfa3 100644 --- a/numpy/core/include/numpy/npy_math.h +++ b/numpy/core/include/numpy/npy_math.h @@ -257,7 +257,7 @@ float npy_nextafterf(float x, float y); float npy_spacingf(float x); /* - * float C99 math functions + * long double C99 math functions */ npy_longdouble npy_sinl(npy_longdouble x); @@ -422,8 +422,18 @@ npy_cdouble npy_ccos(npy_cdouble z); npy_cdouble npy_csin(npy_cdouble z); npy_cdouble npy_ctan(npy_cdouble z); +npy_cdouble npy_ccosh(npy_cdouble z); +npy_cdouble npy_csinh(npy_cdouble z); npy_cdouble npy_ctanh(npy_cdouble z); +npy_cdouble npy_cacos(npy_cdouble z); +npy_cdouble npy_casin(npy_cdouble z); +npy_cdouble npy_catan(npy_cdouble z); + +npy_cdouble npy_cacosh(npy_cdouble z); +npy_cdouble npy_casinh(npy_cdouble z); +npy_cdouble npy_catanh(npy_cdouble z); + /* * Single precision complex functions */ @@ -440,8 +450,19 @@ npy_cfloat npy_ccosf(npy_cfloat z); npy_cfloat npy_csinf(npy_cfloat z); npy_cfloat npy_ctanf(npy_cfloat z); +npy_cfloat npy_ccoshf(npy_cfloat z); +npy_cfloat npy_csinhf(npy_cfloat z); npy_cfloat npy_ctanhf(npy_cfloat z); +npy_cfloat npy_cacosf(npy_cfloat z); +npy_cfloat npy_casinf(npy_cfloat z); +npy_cfloat npy_catanf(npy_cfloat z); + +npy_cfloat npy_cacoshf(npy_cfloat z); +npy_cfloat npy_casinhf(npy_cfloat z); +npy_cfloat npy_catanhf(npy_cfloat z); + + /* * Extended precision complex functions */ @@ -458,8 +479,19 @@ npy_clongdouble npy_ccosl(npy_clongdouble z); npy_clongdouble npy_csinl(npy_clongdouble z); npy_clongdouble npy_ctanl(npy_clongdouble z); +npy_clongdouble npy_ccoshl(npy_clongdouble z); +npy_clongdouble npy_csinhl(npy_clongdouble z); npy_clongdouble npy_ctanhl(npy_clongdouble z); +npy_clongdouble npy_cacosl(npy_clongdouble z); +npy_clongdouble npy_casinl(npy_clongdouble z); +npy_clongdouble npy_catanl(npy_clongdouble z); + +npy_clongdouble npy_cacoshl(npy_clongdouble z); +npy_clongdouble npy_casinhl(npy_clongdouble z); +npy_clongdouble npy_catanhl(npy_clongdouble z); + + /* * Functions that set the floating point error * status word. diff --git a/numpy/core/setup_common.py b/numpy/core/setup_common.py index c10c322d8387..b3a23874b72f 100644 --- a/numpy/core/setup_common.py +++ b/numpy/core/setup_common.py @@ -143,8 +143,9 @@ def check_api_version(apiversion, codegen_dir): C99_COMPLEX_TYPES = ['complex double', 'complex float', 'complex long double'] -C99_COMPLEX_FUNCS = ['creal', 'cimag', 'cabs', 'carg', 'cexp', 'csqrt', 'clog', - 'ccos', 'csin', 'cpow', 'ctan', 'ctanh'] +C99_COMPLEX_FUNCS = ['creal', 'cimag', 'cabs', 'carg', 'cexp', 'clog', 'csqrt', + 'cpow', 'ccos', 'csin', 'ctan', 'ccosh', 'csinh', 'ctanh', + 'cacos', 'casin', 'catan', 'cacosh', 'casinh', 'catanh'] def fname2def(name): return "HAVE_%s" % name.upper() diff --git a/numpy/core/src/npymath/npy_math_complex.c.src b/numpy/core/src/npymath/npy_math_complex.c.src index c2ad5b067e65..97a67d0188d6 100644 --- a/numpy/core/src/npymath/npy_math_complex.c.src +++ b/numpy/core/src/npymath/npy_math_complex.c.src @@ -31,17 +31,87 @@ #include "npy_math_common.h" #include "npy_math_private.h" -/*========================================================== - * Custom implementation of missing complex C99 functions - *=========================================================*/ - /**begin repeat * #type = npy_float, npy_double, npy_longdouble# * #ctype = npy_cfloat,npy_cdouble,npy_clongdouble# * #c = f, , l# * #C = F, , L# * #TMAX = FLT_MAX, DBL_MAX, LDBL_MAX# + * #precision = 1, 2, 3# + */ + +/*========================================================== + * Constants + *=========================================================*/ +static const @ctype@ c_1@c@ = {1.0@C@, 0.0}; +static const @ctype@ c_half@c@ = {0.5@C@, 0.0}; +static const @ctype@ c_i@c@ = {0.0, 1.0@C@}; +static const @ctype@ c_ihalf@c@ = {0.0, 0.5@C@}; + +/*========================================================== + * Helper functions + * + * These are necessary because we do not count on using a + * C99 compiler. + *=========================================================*/ +static NPY_INLINE @ctype@ cadd@c@(@ctype@ a, @ctype@ b) +{ + return npy_cpack@c@(npy_creal@c@(a) + npy_creal@c@(b), + npy_cimag@c@(a) + npy_cimag@c@(b)); +} + +static NPY_INLINE @ctype@ csub@c@(@ctype@ a, @ctype@ b) +{ + return npy_cpack@c@(npy_creal@c@(a) - npy_creal@c@(b), + npy_cimag@c@(a) - npy_cimag@c@(b)); +} + +static NPY_INLINE @ctype@ cmul@c@(@ctype@ a, @ctype@ b) +{ + @type@ ar, ai, br, bi; + ar = npy_creal@c@(a); + ai = npy_cimag@c@(a); + br = npy_creal@c@(b); + bi = npy_cimag@c@(b); + return npy_cpack@c@(ar*br - ai*bi, ar*bi + ai*br); +} + +static NPY_INLINE @ctype@ cdiv@c@(@ctype@ a, @ctype@ b) +{ + @type@ ar, ai, br, bi, d; + ar = npy_creal@c@(a); + ai = npy_cimag@c@(a); + br = npy_creal@c@(b); + bi = npy_cimag@c@(b); + d = br*br + bi*bi; + return npy_cpack@c@((ar*br +ai*bi)/d, (ai*br-ar*bi)/d); +} + +static NPY_INLINE @ctype@ cneg@c@(@ctype@ a) +{ + return npy_cpack@c@(-npy_creal@c@(a), -npy_cimag@c@(a)); +} + +static NPY_INLINE @ctype@ cmuli@c@(@ctype@ a) +{ + return npy_cpack@c@(-npy_cimag@c@(a), npy_creal@c@(a)); +} + +/* + * Perform the operation result := 1 + coef * x * result, + * with real coefficient `coef`. */ +#define SERIES_HORNER_TERM@C@(r, x, c) \ + do { \ + r = cmul@c@(r, x); \ + r = npy_cpack@c@(c*npy_creal@c@(r), c*npy_cimag@c@(r)); \ + r = cadd@c@(c_1@c@, r); \ + } while(0) + +/*========================================================== + * Custom implementation of missing complex C99 functions + *=========================================================*/ + #ifndef HAVE_CABS@C@ @type@ npy_cabs@c@(@ctype@ z) { @@ -193,18 +263,75 @@ #endif #ifndef HAVE_CPOW@C@ -@ctype@ npy_cpow@c@ (@ctype@ x, @ctype@ y) +@ctype@ npy_cpow@c@ (@ctype@ a, @ctype@ b) { - @ctype@ b; - @type@ br, bi, yr, yi; + npy_intp n; + @type@ ar = npy_creal@c@(a); + @type@ br = npy_creal@c@(b); + @type@ ai = npy_cimag@c@(a); + @type@ bi = npy_cimag@c@(b); + @ctype@ loga, r; + + if (br == 0. && bi == 0.) { + return npy_cpack@c@(1., 0.); + } + if (ar == 0. && ai == 0.) { + if (br > 0 && bi == 0) { + return npy_cpack@c@(0., 0.); + } + else { + volatile @type@ tmp = NPY_INFINITY; + /* NB: there are four complex zeros; c0 = (+-0, +-0), so that unlike + * for reals, c0**p, with `p` negative is in general + * ill-defined. + * + * c0**z with z complex is also ill-defined. + */ + r = npy_cpack@c@(NPY_NAN, NPY_NAN); + + /* Raise invalid */ + tmp -= NPY_INFINITY; + ar = tmp; + return r; + } + } + if (bi == 0 && (n=(npy_intp)br) == br) { + if (n == 1) { + /* unroll: handle inf better */ + return npy_cpack@c@(ar, ai); + } + else if (n == 2) { + /* unroll: handle inf better */ + return cmul@c@(a, a); + } + else if (n == 3) { + /* unroll: handle inf better */ + return cmul@c@(a, cmul@c@(a, a)); + } + else if (n > -100 && n < 100) { + @ctype@ p, aa; + npy_intp mask = 1; + if (n < 0) n = -n; + aa = c_1@c@; + p = npy_cpack@c@(ar, ai); + while (1) { + if (n & mask) + aa = cmul@c@(aa,p); + mask <<= 1; + if (n < mask || mask <= 0) break; + p = cmul@c@(p,p); + } + r = npy_cpack@c@(npy_creal@c@(aa), npy_cimag@c@(aa)); + if (br < 0) r = cdiv@c@(c_1@c@, r); + return r; + } + } - yr = npy_creal@c@(y); - yi = npy_cimag@c@(y); - b = npy_clog@c@(x); - br = npy_creal@c@(b); - bi = npy_cimag@c@(b); + loga = npy_clog@c@(a); + ar = npy_creal@c@(loga); + ai = npy_cimag@c@(loga); - return npy_cexp@c@(npy_cpack@c@(br * yr - bi * yi, br * yi + bi * yr)); + return npy_cexp@c@(npy_cpack@c@(ar * br - ai * bi, ar * bi + ai * br)); } #endif @@ -237,6 +364,26 @@ } #endif +#ifndef HAVE_CCOSH@C@ +@ctype@ npy_ccosh@c@(@ctype@ z) +{ + @type@ x, y; + x = npy_creal@c@(z); + y = npy_cimag@c@(z); + return npy_cpack@c@(npy_cos@c@(y)*npy_cosh@c@(x), npy_sin@c@(y)*npy_sinh@c@(x)); +} +#endif + +#ifndef HAVE_CSINH@C@ +@ctype@ npy_csinh@c@(@ctype@ z) +{ + @type@ x, y; + x = npy_creal@c@(z); + y = npy_cimag@c@(z); + return npy_cpack@c@(npy_cos@c@(y)*npy_sinh@c@(x), npy_sin@c@(y)*npy_cosh@c@(x)); +} +#endif + #ifndef HAVE_CTANH@C@ /* * Taken from the msun library in FreeBSD, rev 226600. @@ -345,6 +492,188 @@ #undef TANHF_HUGE #undef TANHL_HUGE #endif + +#ifndef HAVE_CACOS@C@ +@ctype@ npy_cacos@c@(@ctype@ z) +{ + @ctype@ r; + r = cmul@c@(z,z); + r = csub@c@(c_1@c@, r); + r = npy_csqrt@c@(r); + r = cmuli@c@(r); + r = cadd@c@(z, r); + r = npy_clog@c@(r); + r = cmuli@c@(r); + r = cneg@c@(r); + return r; +} +#endif + +#ifndef HAVE_CASIN@C@ +@ctype@ npy_casin@c@(@ctype@ z) +{ + @type@ x, y; + x = npy_creal@c@(z); + y = npy_cimag@c@(z); + + if (npy_fabs(x) > 1e-3 || npy_fabs(y) > 1e-3) { + /* casin(z) = -i * log( i*z + sqrt(1-z**2)) */ + @ctype@ m1, iz, r; + m1 = npy_csqrt@c@(csub@c@(c_1@c@, cmul@c@(z,z))); + iz = cmuli@c@(z); + r = npy_clog@c@(cadd@c@(iz, m1)); + r = cmuli@c@(r); + return cneg@c@(r); + } + else { + /* + * Small arguments: series expansion, to avoid loss of precision + * asin(x) = x [1 + (1/6) x^2 [1 + (9/20) x^2 [1 + ...]]] + * + * |x| < 1e-3 => |rel. error| < 1e-18 (f), 1e-24, 1e-36 (l) + */ + @ctype@ z2, r; + z2 = cmul@c@(z, z); + r = c_1@c@; +#if @precision@ >= 3 + SERIES_HORNER_TERM@C@(r, z2, 81.0@C@/110); + SERIES_HORNER_TERM@C@(r, z2, 49.0@C@/72); +#endif +#if @precision@ >= 2 + SERIES_HORNER_TERM@C@(r, z2, 25.0@C@/42); +#endif + SERIES_HORNER_TERM@C@(r, z2, 9.0@C@/20); + SERIES_HORNER_TERM@C@(r, z2, 1.0@C@/6); + return cmul@c@(r, z); + } +} +#endif + +#ifndef HAVE_CATAN@C@ +@ctype@ npy_catan@c@(@ctype@ z) +{ + @type@ x, y; + x = npy_creal@c@(z); + y = npy_cimag@c@(z); + + if (npy_fabs(x) > 1e-3 || npy_fabs(y) > 1e-3) { + /* catan(z) = 0.5*i * log((i+z)/(i-z)) */ + @ctype@ ip, im; + ip = cadd@c@(c_i@c@, z); + im = csub@c@(c_i@c@, z); + return cmul@c@(c_ihalf@c@, npy_clog@c@(cdiv@c@(ip, im))); + } + else { + /* + * Small arguments: series expansion, to avoid loss of precision + * atan(x) = x [1 - (1/3) x^2 [1 - (3/5) x^2 [1 - ...]]] + * + * |x| < 1e-3 => |rel. error| < 1e-18 (f), 1e-24, 1e-36 (l) + */ + @ctype@ z2, r; + z2 = cmul@c@(z, z); + r = c_1@c@; +#if @precision@ >= 3 + SERIES_HORNER_TERM@C@(r, z2, -9.0@C@/11); + SERIES_HORNER_TERM@C@(r, z2, -7.0@C@/9); +#endif +#if @precision@ >= 2 + SERIES_HORNER_TERM@C@(r, z2, -5.0@C@/7); +#endif + SERIES_HORNER_TERM@C@(r, z2, -3.0@C@/5); + SERIES_HORNER_TERM@C@(r, z2, -1.0@C@/3); + return cmul@c@(r, z); + } +} +#endif + +#ifndef HAVE_CACOSH@C@ +@ctype@ npy_cacosh@c@(@ctype@ z) +{ + /* cacosh(z) = log( z + sqrt(z+1)*sqrt(z-1)) */ + @ctype@ p1, m1; + p1 = npy_csqrt@c@(cadd@c@(z, c_1@c@)); + m1 = npy_csqrt@c@(csub@c@(z, c_1@c@)); + return npy_clog@c@(cadd@c@(z, cmul@c@(p1,m1))); +} +#endif + +#ifndef HAVE_CASINH@C@ +@ctype@ npy_casinh@c@(@ctype@ z) +{ + @type@ x, y; + + x = npy_creal@c@(z); + y = npy_cimag@c@(z); + + if (npy_fabs(x) > 1e-3 || npy_fabs(y) > 1e-3) { + /* casinh(z) = log( z + sqrt(1 + z**2)) */ + @ctype@ p1; + p1 = npy_csqrt@c@(cadd@c@(c_1@c@, cmul@c@(z, z))); + return npy_clog@c@(cadd@c@(p1, z)); + } + else { + /* + * Small arguments: series expansion, to avoid loss of precision + * asinh(x) = x [1 - (1/6) x^2 [1 - (9/20) x^2 [1 - ...]]] + * + * |x| < 1e-3 => |rel. error| < 1e-18 (f), 1e-24, 1e-36 (l) + */ + @ctype@ z2, r; + z2 = cmul@c@(z, z); + r = c_1@c@; +#if @precision@ >= 3 + SERIES_HORNER_TERM@C@(r, z2, -81.0@C@/110); + SERIES_HORNER_TERM@C@(r, z2, -49.0@C@/72); +#endif +#if @precision@ >= 2 + SERIES_HORNER_TERM@C@(r, z2, -25.0@C@/42); +#endif + SERIES_HORNER_TERM@C@(r, z2, -9.0@C@/20); + SERIES_HORNER_TERM@C@(r, z2, -1.0@C@/6); + return cmul@c@(z, r); + } +} +#endif + +#ifndef HAVE_CATANH@C@ +@ctype@ npy_catanh@c@(@ctype@ z) +{ + @type@ x, y; + + x = npy_creal@c@(z); + y = npy_cimag@c@(z); + + if (npy_fabs(x) > 1e-3 || npy_fabs(y) > 1e-3) { + /* catanh(z) = 0.5 * log((1+z)/(1-z)) */ + @ctype@ p1, m1; + p1 = cadd@c@(c_1@c@, z); + m1 = csub@c@(c_1@c@, z); + return cmul@c@(c_half@c@, npy_clog@c@(cdiv@c@(p1, m1))); + } + else { + /* + * Small arguments: series expansion, to avoid loss of precision + * atan(x) = x [1 + (1/3) x^2 [1 + (3/5) x^2 [1 + ...]]] + * + * |x| < 1e-3 => |rel. error| < 1e-18 (f), 1e-24, 1e-36 (l) + */ + @ctype@ z2, r; + z2 = cmul@c@(z, z); + r = c_1@c@; +#if @precision@ >= 3 + SERIES_HORNER_TERM@C@(r, z2, 9.0@C@/11); + SERIES_HORNER_TERM@C@(r, z2, 7.0@C@/9); +#endif +#if @precision@ >= 2 + SERIES_HORNER_TERM@C@(r, z2, 5.0@C@/7); +#endif + SERIES_HORNER_TERM@C@(r, z2, 3.0@C@/5); + SERIES_HORNER_TERM@C@(r, z2, 1.0@C@/3); + return cmul@c@(z, r); + } +} +#endif /**end repeat**/ /*========================================================== @@ -372,8 +701,8 @@ /**end repeat1**/ /**begin repeat1 - * #kind = cexp,clog,csqrt,ccos,csin,ctan,ctanh# - * #KIND = CEXP,CLOG,CSQRT,CCOS,CSIN,CTAN,CTANH# + * #kind = cexp,clog,csqrt,ccos,csin,ctan,ccosh,csinh,ctanh,cacos,casin,catan,cacosh,casinh,catanh# + * #KIND = CEXP,CLOG,CSQRT,CCOS,CSIN,CTAN,CCOSH,CSINH,CTANH,CACOS,CASIN,CATAN,CACOSH,CASINH,CATANH# */ #ifdef HAVE_@KIND@@C@ @ctype@ npy_@kind@@c@(@ctype@ z) diff --git a/numpy/core/src/umath/funcs.inc.src b/numpy/core/src/umath/funcs.inc.src index 15c6620e6184..30059eadc993 100644 --- a/numpy/core/src/umath/funcs.inc.src +++ b/numpy/core/src/umath/funcs.inc.src @@ -206,49 +206,8 @@ npy_ObjectLogicalNot(PyObject *i1) * #ctype = npy_cfloat, npy_cdouble, npy_clongdouble# * #ftype = npy_float, npy_double, npy_longdouble# * #c = f, ,l# - * #C = F, ,L# - * #precision = 1,2,4# */ -/* - * Perform the operation result := 1 + coef * x * result, - * with real coefficient `coef`. - */ -#define SERIES_HORNER_TERM@c@(result, x, coef) \ - do { \ - nc_prod@c@((result), (x), (result)); \ - (result)->real *= (coef); \ - (result)->imag *= (coef); \ - nc_sum@c@((result), &nc_1@c@, (result)); \ - } while(0) - -/* constants */ -static @ctype@ nc_1@c@ = {1., 0.}; -static @ctype@ nc_half@c@ = {0.5, 0.}; -static @ctype@ nc_i@c@ = {0., 1.}; -static @ctype@ nc_i2@c@ = {0., 0.5}; -/* - * static @ctype@ nc_mi@c@ = {0.0@c@, -1.0@c@}; - * static @ctype@ nc_pi2@c@ = {NPY_PI_2@c@., 0.0@c@}; - */ - - -static void -nc_sum@c@(@ctype@ *a, @ctype@ *b, @ctype@ *r) -{ - r->real = a->real + b->real; - r->imag = a->imag + b->imag; - return; -} - -static void -nc_diff@c@(@ctype@ *a, @ctype@ *b, @ctype@ *r) -{ - r->real = a->real - b->real; - r->imag = a->imag - b->imag; - return; -} - static void nc_neg@c@(@ctype@ *a, @ctype@ *r) { @@ -257,26 +216,6 @@ nc_neg@c@(@ctype@ *a, @ctype@ *r) return; } -static void -nc_prod@c@(@ctype@ *a, @ctype@ *b, @ctype@ *r) -{ - @ftype@ ar=a->real, br=b->real, ai=a->imag, bi=b->imag; - r->real = ar*br - ai*bi; - r->imag = ar*bi + ai*br; - return; -} - -static void -nc_quot@c@(@ctype@ *a, @ctype@ *b, @ctype@ *r) -{ - - @ftype@ ar=a->real, br=b->real, ai=a->imag, bi=b->imag; - @ftype@ d = br*br + bi*bi; - r->real = (ar*br + ai*bi)/d; - r->imag = (ai*br - ar*bi)/d; - return; -} - static void nc_sqrt@c@(@ctype@ *x, @ctype@ *r) { @@ -336,164 +275,28 @@ nc_expm1@c@(@ctype@ *x, @ctype@ *r) static void nc_pow@c@(@ctype@ *a, @ctype@ *b, @ctype@ *r) { - npy_intp n; - @ftype@ ar = npy_creal@c@(*a); - @ftype@ br = npy_creal@c@(*b); - @ftype@ ai = npy_cimag@c@(*a); - @ftype@ bi = npy_cimag@c@(*b); - - if (br == 0. && bi == 0.) { - *r = npy_cpack@c@(1., 0.); - return; - } - if (ar == 0. && ai == 0.) { - if (br > 0 && bi == 0) { - *r = npy_cpack@c@(0., 0.); - } - else { - volatile @ftype@ tmp = NPY_INFINITY; - /* NB: there are four complex zeros; c0 = (+-0, +-0), so that unlike - * for reals, c0**p, with `p` negative is in general - * ill-defined. - * - * c0**z with z complex is also ill-defined. - */ - *r = npy_cpack@c@(NPY_NAN, NPY_NAN); - - /* Raise invalid */ - tmp -= NPY_INFINITY; - ar = tmp; - } - return; - } - if (bi == 0 && (n=(npy_intp)br) == br) { - if (n == 1) { - /* unroll: handle inf better */ - *r = npy_cpack@c@(ar, ai); - return; - } - else if (n == 2) { - /* unroll: handle inf better */ - nc_prod@c@(a, a, r); - return; - } - else if (n == 3) { - /* unroll: handle inf better */ - nc_prod@c@(a, a, r); - nc_prod@c@(a, r, r); - return; - } - else if (n > -100 && n < 100) { - @ctype@ p, aa; - npy_intp mask = 1; - if (n < 0) n = -n; - aa = nc_1@c@; - p = npy_cpack@c@(ar, ai); - while (1) { - if (n & mask) - nc_prod@c@(&aa,&p,&aa); - mask <<= 1; - if (n < mask || mask <= 0) break; - nc_prod@c@(&p,&p,&p); - } - *r = npy_cpack@c@(npy_creal@c@(aa), npy_cimag@c@(aa)); - if (br < 0) nc_quot@c@(&nc_1@c@, r, r); - return; - } - } - - *r = npy_cpow@c@(*a, *b); + *r = npy_cpow@c@(*a, *b); return; } - -static void -nc_prodi@c@(@ctype@ *x, @ctype@ *r) -{ - @ftype@ xr = x->real; - r->real = -x->imag; - r->imag = xr; - return; -} - - static void nc_acos@c@(@ctype@ *x, @ctype@ *r) { - /* - * return nc_neg(nc_prodi(nc_log(nc_sum(x,nc_prod(nc_i, - * nc_sqrt(nc_diff(nc_1,nc_prod(x,x)))))))); - */ - nc_prod@c@(x,x,r); - nc_diff@c@(&nc_1@c@, r, r); - nc_sqrt@c@(r, r); - nc_prodi@c@(r, r); - nc_sum@c@(x, r, r); - nc_log@c@(r, r); - nc_prodi@c@(r, r); - nc_neg@c@(r, r); + *r = npy_cacos@c@(*x); return; } static void nc_acosh@c@(@ctype@ *x, @ctype@ *r) { - /* - * return nc_log(nc_sum(x, - * nc_prod(nc_sqrt(nc_sum(x,nc_1)), nc_sqrt(nc_diff(x,nc_1))))); - */ - @ctype@ t; - - nc_sum@c@(x, &nc_1@c@, &t); - nc_sqrt@c@(&t, &t); - nc_diff@c@(x, &nc_1@c@, r); - nc_sqrt@c@(r, r); - nc_prod@c@(&t, r, r); - nc_sum@c@(x, r, r); - nc_log@c@(r, r); + *r = npy_cacosh@c@(*x); return; } static void nc_asin@c@(@ctype@ *x, @ctype@ *r) { - /* - * return nc_neg(nc_prodi(nc_log(nc_sum(nc_prod(nc_i,x), - * nc_sqrt(nc_diff(nc_1,nc_prod(x,x))))))); - */ - if (fabs(x->real) > 1e-3 || fabs(x->imag) > 1e-3) { - @ctype@ a, *pa=&a; - nc_prod@c@(x, x, r); - nc_diff@c@(&nc_1@c@, r, r); - nc_sqrt@c@(r, r); - nc_prodi@c@(x, pa); - nc_sum@c@(pa, r, r); - nc_log@c@(r, r); - nc_prodi@c@(r, r); - nc_neg@c@(r, r); - } - else { - /* - * Small arguments: series expansion, to avoid loss of precision - * asin(x) = x [1 + (1/6) x^2 [1 + (9/20) x^2 [1 + ...]]] - * - * |x| < 1e-3 => |rel. error| < 1e-18 (f), 1e-24, 1e-36 (l) - */ - @ctype@ x2; - nc_prod@c@(x, x, &x2); - - *r = nc_1@c@; -#if @precision@ >= 3 - SERIES_HORNER_TERM@c@(r, &x2, 81.0@C@/110); - SERIES_HORNER_TERM@c@(r, &x2, 49.0@C@/72); -#endif -#if @precision@ >= 2 - SERIES_HORNER_TERM@c@(r, &x2, 25.0@C@/42); -#endif - SERIES_HORNER_TERM@c@(r, &x2, 9.0@C@/20); - SERIES_HORNER_TERM@c@(r, &x2, 1.0@C@/6); - nc_prod@c@(r, x, r); - } + *r = npy_casin@c@(*x); return; } @@ -501,134 +304,35 @@ nc_asin@c@(@ctype@ *x, @ctype@ *r) static void nc_asinh@c@(@ctype@ *x, @ctype@ *r) { - /* - * return nc_log(nc_sum(nc_sqrt(nc_sum(nc_1,nc_prod(x,x))),x)); - */ - if (fabs(x->real) > 1e-3 || fabs(x->imag) > 1e-3) { - nc_prod@c@(x, x, r); - nc_sum@c@(&nc_1@c@, r, r); - nc_sqrt@c@(r, r); - nc_sum@c@(r, x, r); - nc_log@c@(r, r); - } - else { - /* - * Small arguments: series expansion, to avoid loss of precision - * asinh(x) = x [1 - (1/6) x^2 [1 - (9/20) x^2 [1 - ...]]] - * - * |x| < 1e-3 => |rel. error| < 1e-18 (f), 1e-24, 1e-36 (l) - */ - @ctype@ x2; - nc_prod@c@(x, x, &x2); - - *r = nc_1@c@; -#if @precision@ >= 3 - SERIES_HORNER_TERM@c@(r, &x2, -81.0@C@/110); - SERIES_HORNER_TERM@c@(r, &x2, -49.0@C@/72); -#endif -#if @precision@ >= 2 - SERIES_HORNER_TERM@c@(r, &x2, -25.0@C@/42); -#endif - SERIES_HORNER_TERM@c@(r, &x2, -9.0@C@/20); - SERIES_HORNER_TERM@c@(r, &x2, -1.0@C@/6); - nc_prod@c@(r, x, r); - } + *r = npy_casinh@c@(*x); return; } static void nc_atan@c@(@ctype@ *x, @ctype@ *r) { - /* - * return nc_prod(nc_i2,nc_log(nc_quot(nc_sum(nc_i,x),nc_diff(nc_i,x)))); - */ - if (fabs(x->real) > 1e-3 || fabs(x->imag) > 1e-3) { - @ctype@ a, *pa=&a; - nc_diff@c@(&nc_i@c@, x, pa); - nc_sum@c@(&nc_i@c@, x, r); - nc_quot@c@(r, pa, r); - nc_log@c@(r,r); - nc_prod@c@(&nc_i2@c@, r, r); - } - else { - /* - * Small arguments: series expansion, to avoid loss of precision - * atan(x) = x [1 - (1/3) x^2 [1 - (3/5) x^2 [1 - ...]]] - * - * |x| < 1e-3 => |rel. error| < 1e-18 (f), 1e-24, 1e-36 (l) - */ - @ctype@ x2; - nc_prod@c@(x, x, &x2); - - *r = nc_1@c@; -#if @precision@ >= 3 - SERIES_HORNER_TERM@c@(r, &x2, -9.0@C@/11); - SERIES_HORNER_TERM@c@(r, &x2, -7.0@C@/9); -#endif -#if @precision@ >= 2 - SERIES_HORNER_TERM@c@(r, &x2, -5.0@C@/7); -#endif - SERIES_HORNER_TERM@c@(r, &x2, -3.0@C@/5); - SERIES_HORNER_TERM@c@(r, &x2, -1.0@C@/3); - nc_prod@c@(r, x, r); - } + *r = npy_catan@c@(*x); return; } static void nc_atanh@c@(@ctype@ *x, @ctype@ *r) { - /* - * return nc_prod(nc_half,nc_log(nc_quot(nc_sum(nc_1,x),nc_diff(nc_1,x)))); - */ - if (fabs(x->real) > 1e-3 || fabs(x->imag) > 1e-3) { - @ctype@ a, *pa=&a; - nc_diff@c@(&nc_1@c@, x, r); - nc_sum@c@(&nc_1@c@, x, pa); - nc_quot@c@(pa, r, r); - nc_log@c@(r, r); - nc_prod@c@(&nc_half@c@, r, r); - } - else { - /* - * Small arguments: series expansion, to avoid loss of precision - * atan(x) = x [1 + (1/3) x^2 [1 + (3/5) x^2 [1 + ...]]] - * - * |x| < 1e-3 => |rel. error| < 1e-18 (f), 1e-24, 1e-36 (l) - */ - @ctype@ x2; - nc_prod@c@(x, x, &x2); - - *r = nc_1@c@; -#if @precision@ >= 3 - SERIES_HORNER_TERM@c@(r, &x2, 9.0@C@/11); - SERIES_HORNER_TERM@c@(r, &x2, 7.0@C@/9); -#endif -#if @precision@ >= 2 - SERIES_HORNER_TERM@c@(r, &x2, 5.0@C@/7); -#endif - SERIES_HORNER_TERM@c@(r, &x2, 3.0@C@/5); - SERIES_HORNER_TERM@c@(r, &x2, 1.0@C@/3); - nc_prod@c@(r, x, r); - } + *r = npy_catanh@c@(*x); return; } static void nc_cos@c@(@ctype@ *x, @ctype@ *r) { - @ftype@ xr=x->real, xi=x->imag; - r->real = npy_cos@c@(xr)*npy_cosh@c@(xi); - r->imag = -npy_sin@c@(xr)*npy_sinh@c@(xi); + *r = npy_ccos@c@(*x); return; } static void nc_cosh@c@(@ctype@ *x, @ctype@ *r) { - @ftype@ xr=x->real, xi=x->imag; - r->real = npy_cos@c@(xi)*npy_cosh@c@(xr); - r->imag = npy_sin@c@(xi)*npy_sinh@c@(xr); + *r = npy_ccosh@c@(*x); return; } @@ -653,18 +357,14 @@ nc_log2@c@(@ctype@ *x, @ctype@ *r) static void nc_sin@c@(@ctype@ *x, @ctype@ *r) { - @ftype@ xr=x->real, xi=x->imag; - r->real = npy_sin@c@(xr)*npy_cosh@c@(xi); - r->imag = npy_cos@c@(xr)*npy_sinh@c@(xi); + *r = npy_csin@c@(*x); return; } static void nc_sinh@c@(@ctype@ *x, @ctype@ *r) { - @ftype@ xr=x->real, xi=x->imag; - r->real = npy_cos@c@(xi)*npy_sinh@c@(xr); - r->imag = npy_sin@c@(xi)*npy_cosh@c@(xr); + *r = npy_csinh@c@(*x); return; } @@ -682,6 +382,4 @@ nc_tanh@c@(@ctype@ *x, @ctype@ *r) return; } -#undef SERIES_HORNER_TERM@c@ - /**end repeat**/ From af5ff5510b8477226b4c204a3c99be8c96a072af Mon Sep 17 00:00:00 2001 From: Eric Moore Date: Thu, 7 Mar 2013 19:51:54 -0500 Subject: [PATCH 04/35] ENH: npymath: Use a better complex division algorithm. Using the same technique used for divide complex arrays, copied from umath/loops.c.src. --- numpy/core/src/npymath/npy_math_complex.c.src | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/numpy/core/src/npymath/npy_math_complex.c.src b/numpy/core/src/npymath/npy_math_complex.c.src index 97a67d0188d6..af58dce1c167 100644 --- a/numpy/core/src/npymath/npy_math_complex.c.src +++ b/numpy/core/src/npymath/npy_math_complex.c.src @@ -78,13 +78,30 @@ static NPY_INLINE @ctype@ cmul@c@(@ctype@ a, @ctype@ b) static NPY_INLINE @ctype@ cdiv@c@(@ctype@ a, @ctype@ b) { - @type@ ar, ai, br, bi, d; + @type@ ar, ai, br, bi, abs_br, abs_bi; ar = npy_creal@c@(a); ai = npy_cimag@c@(a); br = npy_creal@c@(b); bi = npy_cimag@c@(b); - d = br*br + bi*bi; - return npy_cpack@c@((ar*br +ai*bi)/d, (ai*br-ar*bi)/d); + abs_br = npy_fabs@c@(br); + abs_bi = npy_fabs@c@(bi); + + if (abs_br >= abs_bi) { + if (abs_br == 0 && abs_bi == 0) { + /* divide by zeros should yield a complex inf or nan */ + return npy_cpack@c@(ar/abs_br, ai/abs_bi); + } + else { + @type@ rat = bi/br; + @type@ scl = 1.0@C@/(br+bi*rat); + return npy_cpack@c@((ar + ai*rat)*scl, (ai - ar*rat)*scl); + } + } + else { + @type@ rat = br/bi; + @type@ scl = 1.0@C@/(bi + br*rat); + return npy_cpack@c@((ar*rat + ai)*scl, (ai*rat - ar)*scl); + } } static NPY_INLINE @ctype@ cneg@c@(@ctype@ a) From e6b99c946c411b3ae4e8b1dbdab2149ed341fa19 Mon Sep 17 00:00:00 2001 From: Eric Moore Date: Sat, 9 Mar 2013 11:40:03 -0500 Subject: [PATCH 05/35] MAINT: umath: generate the ldexp and frexp ufuncs like all the others. --- numpy/core/code_generators/generate_umath.py | 38 ++++++- .../core/code_generators/ufunc_docstrings.py | 62 +++++++++++ numpy/core/src/umath/umathmodule.c | 100 +----------------- 3 files changed, 103 insertions(+), 97 deletions(-) diff --git a/numpy/core/code_generators/generate_umath.py b/numpy/core/code_generators/generate_umath.py index e3c9cf28bee0..1d2dc270c1dc 100644 --- a/numpy/core/code_generators/generate_umath.py +++ b/numpy/core/code_generators/generate_umath.py @@ -20,6 +20,12 @@ class FullTypeDescr(object): pass +class FuncNameSuffix(object): + """Stores the suffix to append when generating function names. + """ + def __init__(self, suffix): + self.suffix = suffix + class TypeDescription(object): """Type signature for a ufunc. @@ -27,7 +33,7 @@ class TypeDescription(object): ---------- type : str Character representing the nominal type. - func_data : str or None or FullTypeDescr, optional + func_data : str or None or FullTypeDescr or FuncNameSuffix, optional The string representing the expression to insert into the data array, if any. in_ : str or None, optional @@ -795,6 +801,30 @@ def english_upper(s): None, TD(flts), ), +'ldexp' : + Ufunc(2, 1, None, + docstrings.get('numpy.core.umath.ldexp'), + None, + [TypeDescription('e', None, 'ei', 'e'), + TypeDescription('f', None, 'fi', 'f'), + TypeDescription('e', FuncNameSuffix('long'), 'el', 'e'), + TypeDescription('f', FuncNameSuffix('long'), 'fl', 'f'), + TypeDescription('d', None, 'di', 'd'), + TypeDescription('d', FuncNameSuffix('long'), 'dl', 'd'), + TypeDescription('g', None, 'gi', 'g'), + TypeDescription('g', FuncNameSuffix('long'), 'gl', 'g'), + ], + ), +'frexp' : + Ufunc(1, 2, None, + docstrings.get('numpy.core.umath.frexp'), + None, + [TypeDescription('e', None, 'e', 'ei'), + TypeDescription('f', None, 'f', 'fi'), + TypeDescription('d', None, 'd', 'di'), + TypeDescription('g', None, 'g', 'gi'), + ], + ) } if sys.version_info[0] >= 3: @@ -854,7 +884,7 @@ def make_arrays(funcdict): thedict = chartotype1 # one input and one output for t in uf.type_descriptions: - if t.func_data not in (None, FullTypeDescr): + if t.func_data not in (None, FullTypeDescr) and not isinstance(t.func_data, FuncNameSuffix): funclist.append('NULL') astype = '' if not t.astype is None: @@ -880,6 +910,10 @@ def make_arrays(funcdict): tname = english_upper(chartoname[t.type]) datalist.append('(void *)NULL') funclist.append('%s_%s_%s_%s' % (tname, t.in_, t.out, name)) + elif isinstance(t.func_data, FuncNameSuffix): + datalist.append('(void *)NULL') + tname = english_upper(chartoname[t.type]) + funclist.append('%s_%s_%s' % (tname, name, t.func_data.suffix)) else: datalist.append('(void *)NULL') tname = english_upper(chartoname[t.type]) diff --git a/numpy/core/code_generators/ufunc_docstrings.py b/numpy/core/code_generators/ufunc_docstrings.py index 59e8777506f0..28564d7af343 100644 --- a/numpy/core/code_generators/ufunc_docstrings.py +++ b/numpy/core/code_generators/ufunc_docstrings.py @@ -3312,3 +3312,65 @@ def add_newdoc(place, name, doc): array([0, 0, 0, 0, 1]) """) + +add_newdoc('numpy.core.umath', 'ldexp', + """ + Scale floating point value by a power of 2, element-wise. + + Equivalent to ``x1 * 2**x2``. + + Parameters + ---------- + x1 : array_like + Input array to be scaled. + x2 : array_like + Input array of exponents. + + Returns + ------- + y: ndarray + The corresponding scaled values. + + See Also + -------- + frexp : Extract significand and exponent from floating point number + + Examples + -------- + >>> np.ldexp(0.5, np.arange(-1,4)) + array([ 0.25, 0.5 , 1. , 2. , 4. ]) + + """) + +add_newdoc('numpy.core.umath', 'frexp', + """ + Extract significand and exponent from a floating point number. + + All floating point numbers are of the form: ``y1 * 2**y2`` where `y1`, + the significand, is a number between 0.5 and 1, or 0 and `y2`, the + exponent, is an integer. + + Parameters + ---------- + x: array_like + Input array. + + Returns + ------- + y1 : ndarray + The normalized significands from `x`. + y2 : ndarray + The exponents from `x`. + + See Also + -------- + ldexp : Scale floating point number by power of 2 + + Examples + -------- + >>> np.frexp([0.25, 0.5, 1, 2, 4]) + (array([ 0.5, 0.5, 0.5, 0.5, 0.5]), + array([-1, 0, 1, 2, 3], dtype=int32)) + + """) + diff --git a/numpy/core/src/umath/umathmodule.c b/numpy/core/src/umath/umathmodule.c index f395d509671c..25295a2812b1 100644 --- a/numpy/core/src/umath/umathmodule.c +++ b/numpy/core/src/umath/umathmodule.c @@ -201,99 +201,6 @@ ufunc_frompyfunc(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *NPY_UNUS ** SETUP UFUNCS ** ***************************************************************************** */ - -/* Less automated additions to the ufuncs */ - -static PyUFuncGenericFunction frexp_functions[] = { -#ifdef HAVE_FREXPF - HALF_frexp, - FLOAT_frexp, -#endif - DOUBLE_frexp -#ifdef HAVE_FREXPL - ,LONGDOUBLE_frexp -#endif -}; - -static void * blank3_data[] = { (void *)NULL, (void *)NULL, (void *)NULL}; -static void * blank6_data[] = { (void *)NULL, (void *)NULL, (void *)NULL, - (void *)NULL, (void *)NULL, (void *)NULL}; -static char frexp_signatures[] = { -#ifdef HAVE_FREXPF - NPY_HALF, NPY_HALF, NPY_INT, - NPY_FLOAT, NPY_FLOAT, NPY_INT, -#endif - NPY_DOUBLE, NPY_DOUBLE, NPY_INT -#ifdef HAVE_FREXPL - ,NPY_LONGDOUBLE, NPY_LONGDOUBLE, NPY_INT -#endif -}; - -#if NPY_SIZEOF_LONG == NPY_SIZEOF_INT -#define LDEXP_LONG(typ) typ##_ldexp -#else -#define LDEXP_LONG(typ) typ##_ldexp_long -#endif - -static PyUFuncGenericFunction ldexp_functions[] = { -#ifdef HAVE_LDEXPF - HALF_ldexp, - FLOAT_ldexp, - LDEXP_LONG(HALF), - LDEXP_LONG(FLOAT), -#endif - DOUBLE_ldexp, - LDEXP_LONG(DOUBLE) -#ifdef HAVE_LDEXPL - , - LONGDOUBLE_ldexp, - LDEXP_LONG(LONGDOUBLE) -#endif -}; - -static char ldexp_signatures[] = { -#ifdef HAVE_LDEXPF - NPY_HALF, NPY_INT, NPY_HALF, - NPY_FLOAT, NPY_INT, NPY_FLOAT, - NPY_HALF, NPY_LONG, NPY_HALF, - NPY_FLOAT, NPY_LONG, NPY_FLOAT, -#endif - NPY_DOUBLE, NPY_INT, NPY_DOUBLE, - NPY_DOUBLE, NPY_LONG, NPY_DOUBLE -#ifdef HAVE_LDEXPL - ,NPY_LONGDOUBLE, NPY_INT, NPY_LONGDOUBLE - ,NPY_LONGDOUBLE, NPY_LONG, NPY_LONGDOUBLE -#endif -}; - -static void -InitOtherOperators(PyObject *dictionary) { - PyObject *f; - int num; - - num = sizeof(frexp_functions) / sizeof(frexp_functions[0]); - f = PyUFunc_FromFuncAndData(frexp_functions, blank3_data, - frexp_signatures, num, - 1, 2, PyUFunc_None, "frexp", - "Split the number, x, into a normalized"\ - " fraction (y1) and exponent (y2)",0); - PyDict_SetItemString(dictionary, "frexp", f); - Py_DECREF(f); - - num = sizeof(ldexp_functions) / sizeof(ldexp_functions[0]); - f = PyUFunc_FromFuncAndData(ldexp_functions, blank6_data, ldexp_signatures, num, - 2, 1, PyUFunc_None, "ldexp", - "Compute y = x1 * 2**x2.",0); - PyDict_SetItemString(dictionary, "ldexp", f); - Py_DECREF(f); - -#if defined(NPY_PY3K) - f = PyDict_GetItemString(dictionary, "true_divide"); - PyDict_SetItemString(dictionary, "divide", f); -#endif - return; -} - NPY_VISIBILITY_HIDDEN PyObject * npy_um_str_out = NULL; NPY_VISIBILITY_HIDDEN PyObject * npy_um_str_subok = NULL; NPY_VISIBILITY_HIDDEN PyObject * npy_um_str_array_prepare = NULL; @@ -409,8 +316,6 @@ PyMODINIT_FUNC initumath(void) /* Load the ufunc operators into the array module's namespace */ InitOperators(d); - InitOtherOperators(d); - PyDict_SetItemString(d, "pi", s = PyFloat_FromDouble(NPY_PI)); Py_DECREF(s); PyDict_SetItemString(d, "e", s = PyFloat_FromDouble(NPY_E)); @@ -418,6 +323,11 @@ PyMODINIT_FUNC initumath(void) PyDict_SetItemString(d, "euler_gamma", s = PyFloat_FromDouble(NPY_EULER)); Py_DECREF(s); +#if defined(NPY_PY3K) + s = PyDict_GetItemString(d, "true_divide"); + PyDict_SetItemString(d, "divide", s); +#endif + #define ADDCONST(str) PyModule_AddIntConstant(m, #str, UFUNC_##str) #define ADDSCONST(str) PyModule_AddStringConstant(m, "UFUNC_" #str, UFUNC_##str) From 0fdaa54df1d724109522abab328c61d1fffc0744 Mon Sep 17 00:00:00 2001 From: Eric Moore Date: Sat, 9 Mar 2013 17:12:36 -0500 Subject: [PATCH 06/35] MAINT: umath/npymath: Add ldexp and frexp to npymath npy_frexp and npy_ldexp are used unconditionally in umath. (i.e. HAVE_LDEXPF, etc. no longer appears in umath.) --- numpy/core/include/numpy/npy_math.h | 9 +++++++ numpy/core/src/npymath/npy_math.c.src | 35 +++++++++++++++++++++++++++ numpy/core/src/umath/loops.c.src | 28 ++++++++------------- numpy/core/src/umath/loops.h.src | 4 --- 4 files changed, 54 insertions(+), 22 deletions(-) diff --git a/numpy/core/include/numpy/npy_math.h b/numpy/core/include/numpy/npy_math.h index f91e17c7cfa3..357960113eed 100644 --- a/numpy/core/include/numpy/npy_math.h +++ b/numpy/core/include/numpy/npy_math.h @@ -152,6 +152,9 @@ double npy_copysign(double x, double y); double npy_nextafter(double x, double y); double npy_spacing(double x); +double npy_ldexp(double x, int exp); +double npy_frexp(double x, int* exp); + /* * IEEE 754 fpu handling. Those are guaranteed to be macros */ @@ -256,6 +259,9 @@ float npy_copysignf(float x, float y); float npy_nextafterf(float x, float y); float npy_spacingf(float x); +float npy_ldexpf(float x, int exp); +float npy_frexpf(float x, int* exp); + /* * long double C99 math functions */ @@ -297,6 +303,9 @@ npy_longdouble npy_copysignl(npy_longdouble x, npy_longdouble y); npy_longdouble npy_nextafterl(npy_longdouble x, npy_longdouble y); npy_longdouble npy_spacingl(npy_longdouble x); +npy_longdouble npy_ldexpl(npy_longdouble x, int exp); +npy_longdouble npy_frexpl(npy_longdouble x, int* exp); + /* * Non standard functions */ diff --git a/numpy/core/src/npymath/npy_math.c.src b/numpy/core/src/npymath/npy_math.c.src index 61f1d79abe9d..ebc209fc2992 100644 --- a/numpy/core/src/npymath/npy_math.c.src +++ b/numpy/core/src/npymath/npy_math.c.src @@ -305,6 +305,7 @@ double npy_log2(double x) * asinh, acosh, atanh * * hypot, atan2, pow, fmod, modf + * ldexp, frexp * * We assume the above are always available in their double versions. * @@ -367,6 +368,26 @@ double npy_log2(double x) } #endif +#ifdef ldexp@c@ +#undef ldexp@c@ +#endif +#ifndef HAVE_LDEXP@C@ +@type@ npy_ldexp@c@(@type@ x, npy_int exp) +{ + return (@type@) npy_ldexp((double)x, exp); +} +#endif + +#ifdef frexp@c@ +#undef frexp@c@ +#endif +#ifndef HAVE_FREXP@C@ +@type@ npy_frexp@c@(@type@ x, npy_int* exp) +{ + return (@type@) npy_frexp(x, exp); +} +#endif + /**end repeat**/ @@ -413,6 +434,20 @@ double npy_log2(double x) } #endif +#ifdef HAVE_LDEXP@C@ +@type@ npy_ldexp@c@(@type@ x, npy_int exp) +{ + return ldexp@c@(x, exp); +} +#endif + +#ifdef HAVE_FREXP@C@ +@type@ npy_frexp@c@(@type@ x, npy_int* exp) +{ + return frexp@c@(x, exp); +} +#endif + /**end repeat**/ diff --git a/numpy/core/src/umath/loops.c.src b/numpy/core/src/umath/loops.c.src index 43419780e768..2fcb11d547da 100644 --- a/numpy/core/src/umath/loops.c.src +++ b/numpy/core/src/umath/loops.c.src @@ -1717,25 +1717,22 @@ NPY_NO_EXPORT void } } -#ifdef HAVE_FREXP@C@ NPY_NO_EXPORT void @TYPE@_frexp(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) { UNARY_LOOP_TWO_OUT { const @type@ in1 = *(@type@ *)ip1; - *((@type@ *)op1) = frexp@c@(in1, (int *)op2); + *((@type@ *)op1) = npy_frexp@c@(in1, (int *)op2); } } -#endif -#ifdef HAVE_LDEXP@C@ NPY_NO_EXPORT void @TYPE@_ldexp(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { const @type@ in1 = *(@type@ *)ip1; const int in2 = *(int *)ip2; - *((@type@ *)op1) = ldexp@c@(in1, in2); + *((@type@ *)op1) = npy_ldexp@c@(in1, in2); } } @@ -1752,7 +1749,7 @@ NPY_NO_EXPORT void const long in2 = *(long *)ip2; if (((int)in2) == in2) { /* Range OK */ - *((@type@ *)op1) = ldexp@c@(in1, ((int)in2)); + *((@type@ *)op1) = npy_ldexp@c@(in1, ((int)in2)); } else { /* @@ -1760,15 +1757,14 @@ NPY_NO_EXPORT void * given that exponent has less bits than npy_int. */ if (in2 > 0) { - *((@type@ *)op1) = ldexp@c@(in1, NPY_MAX_INT); + *((@type@ *)op1) = npy_ldexp@c@(in1, NPY_MAX_INT); } else { - *((@type@ *)op1) = ldexp@c@(in1, NPY_MIN_INT); + *((@type@ *)op1) = npy_ldexp@c@(in1, NPY_MIN_INT); } } } } -#endif #define @TYPE@_true_divide @TYPE@_divide @@ -2033,25 +2029,22 @@ HALF_modf(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(f } } -#ifdef HAVE_FREXPF NPY_NO_EXPORT void HALF_frexp(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) { UNARY_LOOP_TWO_OUT { const float in1 = npy_half_to_float(*(npy_half *)ip1); - *((npy_half *)op1) = npy_float_to_half(frexpf(in1, (int *)op2)); + *((npy_half *)op1) = npy_float_to_half(npy_frexpf(in1, (int *)op2)); } } -#endif -#ifdef HAVE_LDEXPF NPY_NO_EXPORT void HALF_ldexp(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { const float in1 = npy_half_to_float(*(npy_half *)ip1); const int in2 = *(int *)ip2; - *((npy_half *)op1) = npy_float_to_half(ldexpf(in1, in2)); + *((npy_half *)op1) = npy_float_to_half(npy_ldexpf(in1, in2)); } } @@ -2068,7 +2061,7 @@ HALF_ldexp_long(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UN const long in2 = *(long *)ip2; if (((int)in2) == in2) { /* Range OK */ - *((npy_half *)op1) = npy_float_to_half(ldexpf(in1, ((int)in2))); + *((npy_half *)op1) = npy_float_to_half(npy_ldexpf(in1, ((int)in2))); } else { /* @@ -2076,15 +2069,14 @@ HALF_ldexp_long(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UN * given that exponent has less bits than npy_int. */ if (in2 > 0) { - *((npy_half *)op1) = npy_float_to_half(ldexpf(in1, NPY_MAX_INT)); + *((npy_half *)op1) = npy_float_to_half(npy_ldexpf(in1, NPY_MAX_INT)); } else { - *((npy_half *)op1) = npy_float_to_half(ldexpf(in1, NPY_MIN_INT)); + *((npy_half *)op1) = npy_float_to_half(npy_ldexpf(in1, NPY_MIN_INT)); } } } } -#endif #define HALF_true_divide HALF_divide diff --git a/numpy/core/src/umath/loops.h.src b/numpy/core/src/umath/loops.h.src index a8a58c5def1e..65d7056e7b8f 100644 --- a/numpy/core/src/umath/loops.h.src +++ b/numpy/core/src/umath/loops.h.src @@ -277,17 +277,13 @@ NPY_NO_EXPORT void NPY_NO_EXPORT void @TYPE@_modf(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#ifdef HAVE_FREXP@C@ NPY_NO_EXPORT void @TYPE@_frexp(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#endif -#ifdef HAVE_LDEXP@C@ NPY_NO_EXPORT void @TYPE@_ldexp(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void @TYPE@_ldexp_long(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#endif #define @TYPE@_true_divide @TYPE@_divide From 2d33f7c8ecedead77a9b823c560f3a94a0131303 Mon Sep 17 00:00:00 2001 From: Eric Moore Date: Sun, 10 Mar 2013 09:52:03 -0400 Subject: [PATCH 07/35] ENH: npymath: handle clog edge cases more carefully. --- numpy/core/src/npymath/npy_math_complex.c.src | 67 ++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/numpy/core/src/npymath/npy_math_complex.c.src b/numpy/core/src/npymath/npy_math_complex.c.src index af58dce1c167..113ee6405cac 100644 --- a/numpy/core/src/npymath/npy_math_complex.c.src +++ b/numpy/core/src/npymath/npy_math_complex.c.src @@ -1,4 +1,6 @@ /* + * vim: syntax=c + * * Implement some C99-compatible complex math functions * * Most of the code is taken from the msun library in FreeBSD (HEAD @ 30th June @@ -37,6 +39,8 @@ * #c = f, , l# * #C = F, , L# * #TMAX = FLT_MAX, DBL_MAX, LDBL_MAX# + * #TMIN = FLT_MIN, DBL_MIN, LDBL_MIN# + * #TMANT_DIG = FLT_MANT_DIG, DBL_MANT_DIG, LDBL_MANT_DIG# * #precision = 1, 2, 3# */ @@ -205,9 +209,70 @@ static NPY_INLINE @ctype@ cmuli@c@(@ctype@ a) #endif #ifndef HAVE_CLOG@C@ +/* algorithm from cpython, rev. d86f5686cef9 + * + * The usual formula for the real part is log(hypot(z.real, z.imag)). + * There are four situations where this formula is potentially + * problematic: + * + * (1) the absolute value of z is subnormal. Then hypot is subnormal, + * so has fewer than the usual number of bits of accuracy, hence may + * have large relative error. This then gives a large absolute error + * in the log. This can be solved by rescaling z by a suitable power + * of 2. + * + * (2) the absolute value of z is greater than DBL_MAX (e.g. when both + * z.real and z.imag are within a factor of 1/sqrt(2) of DBL_MAX) + * Again, rescaling solves this. + * + * (3) the absolute value of z is close to 1. In this case it's + * difficult to achieve good accuracy, at least in part because a + * change of 1ulp in the real or imaginary part of z can result in a + * change of billions of ulps in the correctly rounded answer. + * + * (4) z = 0. The simplest thing to do here is to call the + * floating-point log with an argument of 0, and let its behaviour + * (returning -infinity, signaling a floating-point exception, setting + * errno, or whatever) determine that of c_log. So the usual formula + * is fine here. +*/ @ctype@ npy_clog@c@(@ctype@ z) { - return npy_cpack@c@(npy_log@c@ (npy_cabs@c@ (z)), npy_carg@c@ (z)); + @type@ ax = npy_fabs@c@(npy_creal@c@(z)); + @type@ ay = npy_fabs@c@(npy_cimag@c@(z)); + @type@ rr, ri; + + if (ax > @TMAX@/4 || ay > @TMAX@/4) { + rr = npy_log@c@(npy_hypot@c@(ax/2, ay/2)) + NPY_LOGE2@c@; + } + else if (ax < @TMIN@ && ay < @TMIN@) { + if (ax > 0 || ay > 0) { + /* catch cases where hypot(ax, ay) is subnormal */ + rr = npy_log@c@(npy_hypot@c@(npy_ldexp@c@(ax, @TMANT_DIG@), + npy_ldexp@c@(ay, @TMANT_DIG@))) - @TMANT_DIG@*NPY_LOGE2@c@; + } + else { + /* log(+/-0 +/- 0i) */ + /* raise divide-by-zero floating point exception */ + rr = -1.0@c@ / npy_creal@c@(z); + rr = npy_copysign@c@(rr, -1); + ri = npy_carg@c@(z); + return npy_cpack@c@(rr, ri); + } + } + else { + @type@ h = npy_hypot@c@(ax, ay); + if (0.71 <= h && h <= 1.73) { + @type@ am = ax > ay ? ax : ay; /* max(ax, ay) */ + @type@ an = ax > ay ? ay : ax; /* min(ax, ay) */ + rr = npy_log1p@c@((am-1)*(am+1)+an*an)/2; + } + else { + rr = npy_log@c@(h); + } + } + ri = npy_carg@c@(z); + return npy_cpack@c@(rr, ri); } #endif From 50d6720dde486bfb9873431bfa3f51316d51465f Mon Sep 17 00:00:00 2001 From: Eric Moore Date: Mon, 30 Sep 2013 21:17:55 -0400 Subject: [PATCH 08/35] ENH: evaluate c99 complex funcs at build time For all of the C99 complex function excepting the creal, cimag, cabs and carg (which are either trivial or defined by a C89 function) we now run a set of build time tests to evaluate if we want to use the system version of these functions or our own. These tests involve all of the special values for these functions defined in Annxex G of the C99 standard as well as any tests that were existing in the numpy test suite. These tests do not consider the sign of NAN. --- numpy/core/setup.py | 23 + numpy/core/setup_common.py | 7 +- numpy/core/test_c99complex.c | 1589 ++++++++++++++++++++++++++++++++++ 3 files changed, 1616 insertions(+), 3 deletions(-) create mode 100644 numpy/core/test_c99complex.c diff --git a/numpy/core/setup.py b/numpy/core/setup.py index a93311ec5ccd..add5af755597 100644 --- a/numpy/core/setup.py +++ b/numpy/core/setup.py @@ -220,6 +220,29 @@ def check_prec(prec): else: priv.extend([(fname2def(f), 1) for f in flist]) + flist = [f + prec for f in C99_COMPLEX_FUNCS_CHECKED] + decl = dict([(f, True) for f in flist]) + exists = [] + if not config.check_funcs_once(flist, call=decl, decl=decl, + libraries=mathlibs): + for f in C99_COMPLEX_FUNCS_CHECKED: + if config.check_func(f + prec, call=True, decl=True, + libraries=mathlibs): + exists.append(f) + else: + exists.extend(C99_COMPLEX_FUNCS_CHECKED) + + if len(exists) > 0: + fp = open('./numpy/core/test_c99complex.c', 'r') + obody = fp.read() + fp.close() + precname = {'f':'FLOAT', '':'DOUBLE', 'l':'LONGDOUBLE'}[prec] + for f in exists: + print("ERIC1") + body = obody.replace('PYTESTPRECISION', precname).replace('PYTESTFUNC', f.upper()) + if config.try_run(body, libraries=mathlibs): + priv.append((fname2def(f + prec), 1)) + check_prec('') check_prec('f') check_prec('l') diff --git a/numpy/core/setup_common.py b/numpy/core/setup_common.py index b3a23874b72f..fc4452d419c1 100644 --- a/numpy/core/setup_common.py +++ b/numpy/core/setup_common.py @@ -143,9 +143,10 @@ def check_api_version(apiversion, codegen_dir): C99_COMPLEX_TYPES = ['complex double', 'complex float', 'complex long double'] -C99_COMPLEX_FUNCS = ['creal', 'cimag', 'cabs', 'carg', 'cexp', 'clog', 'csqrt', - 'cpow', 'ccos', 'csin', 'ctan', 'ccosh', 'csinh', 'ctanh', - 'cacos', 'casin', 'catan', 'cacosh', 'casinh', 'catanh'] +C99_COMPLEX_FUNCS = ['creal', 'cimag', 'cabs', 'carg'] +C99_COMPLEX_FUNCS_CHECKED = ['cacos', 'casin', 'catan', 'cacosh', 'casinh', + 'catanh', 'ccos', 'csin', 'ctan', 'ccosh', 'csinh', + 'ctanh', 'cexp', 'clog', 'cpow', 'csqrt'] def fname2def(name): return "HAVE_%s" % name.upper() diff --git a/numpy/core/test_c99complex.c b/numpy/core/test_c99complex.c new file mode 100644 index 000000000000..c8add9745ac5 --- /dev/null +++ b/numpy/core/test_c99complex.c @@ -0,0 +1,1589 @@ +#include +#include +#include +#include +#include + +#define PYTESTPRECISION +#define PYTESTFUNC + +#ifdef FLOAT +#define TYPE float +#define SUFFIX f +#define EPS FLT_EPSILON +#define CLOSE_ATOL 3 +#define CLOSE_RTOL 1e-5 +#define FMT "%.8e" +#define NPY_PI_2 1.570796326794896619231321691639751442f +#define NPY_PI 3.141592653589793238462643383279502884f +#define NPY_LOG2E 1.442695040888963407359924681001892137f +#define NPY_SQRT2 1.414213562373095048801688724209698079f +#else +#ifdef DOUBLE +#define TYPE double +#define SUFFIX +#define EPS DBL_EPSILON +#define CLOSE_ATOL 0 +#define CLOSE_RTOL 1e-12 +#define FMT "%.16e" +#define NPY_PI_2 1.570796326794896619231321691639751442 +#define NPY_PI 3.141592653589793238462643383279502884 +#define NPY_LOG2E 1.442695040888963407359924681001892137 +#define NPY_SQRT2 1.414213562373095048801688724209698079 +#else +#ifdef LONGDOUBLE +#define TYPE long double +#define SUFFIX l +#define EPS 50*LDBL_EPSILON +#define CLOSE_ATOL 0 +#define CLOSE_RTOL 1e-12 +#define FMT "%.18Le" +#define NPY_PI_2 1.570796326794896619231321691639751442L +#define NPY_PI 3.141592653589793238462643383279502884L +#define NPY_LOG2E 1.442695040888963407359924681001892137L +#define NPY_SQRT2 1.414213562373095048801688724209698079L +#else +#error "Define FLOAT or DOUBLE or LONGDOUBLE" +#endif +#endif +#endif + +const TYPE NZERO = -1.0 * 0.0; + +#define STRINGIZE_INT(A) #A +#define STRINGIZE(A) STRINGIZE_INT(A) + +#define CONCAT(A, B) A ## B +#define ADDSUFFIX_INT(A, B) CONCAT(A, B) +#define ADDSUFFIX(A) ADDSUFFIX_INT(A, SUFFIX) + +#define TEST_PRINTF(func, xr, xi, er, ei, rr, ri) \ + printf("%d: " STRINGIZE(func) STRINGIZE(SUFFIX) "(" FMT " + " FMT "j): " \ + "expected: " FMT " + " FMT "j: received: " FMT " + " FMT "j\n", \ + __LINE__, xr, xi, er, ei, rr, ri) + +#define TEST_INT(func, xr, xi, er, ei, rtest, itest) \ + do { \ + TYPE dxr = xr; \ + TYPE dxi = xi; \ + TYPE der = er; \ + TYPE dei = ei; \ + TYPE complex x = cpack(dxr, dxi); \ + TYPE complex r = ADDSUFFIX(func)(x); \ + TYPE rr = ADDSUFFIX(creal)(r); \ + TYPE ri = ADDSUFFIX(cimag)(r); \ + if (!(rtest(rr, der) && itest(ri, dei))) { \ + ret = 0; \ + TEST_PRINTF(func, dxr, dxi, der, dei, rr, ri); \ + } \ + } \ + while(0) + +#define TEST_EE(func, xr, xi, er, ei) \ + TEST_INT(func, xr, xi, er, ei, isequal, isequal) + +#define TEST_EC(func, xr, xi, er, ei) \ + TEST_INT(func, xr, xi, er, ei, isequal, isclose) + +#define TEST_CE(func, xr, xi, er, ei) \ + TEST_INT(func, xr, xi, er, ei, isclose, isequal) + +#define TEST_CC(func, xr, xi, er, ei) \ + TEST_INT(func, xr, xi, er, ei, isclose, isclose) + +#define TEST_UNSPECIFIED2(func, xr, xi, er1, ei1, er2, ei2) \ + do { \ + TYPE dxr = xr; \ + TYPE dxi = xi; \ + TYPE der1 = er1; \ + TYPE dei1 = ei1; \ + TYPE der2 = er2; \ + TYPE dei2 = ei2; \ + TYPE complex x = cpack(dxr, dxi); \ + TYPE complex r = ADDSUFFIX(func)(x); \ + TYPE rr = ADDSUFFIX(creal)(r); \ + TYPE ri = ADDSUFFIX(cimag)(r); \ + if (!((isequal(rr, der1) && isequal(ri, dei1)) || \ + (isequal(rr, der2) && isequal(ri, dei2)))) { \ + ret = 0; \ + TEST_PRINTF(func, dxr, dxi, der1, dei1, rr, ri); \ + printf("or"); \ + TEST_PRINTF(func, dxr, dxi, der2, dei2, rr, ri); \ + } \ + } \ + while(0) + +#define TEST_UNSPECIFIED4(func, xr, xi, er1, ei1, er2, ei2, er3, ei3, er4, ei4)\ + do { \ + TYPE dxr = xr; \ + TYPE dxi = xi; \ + TYPE der1 = er1; \ + TYPE dei1 = ei1; \ + TYPE der2 = er2; \ + TYPE dei2 = ei2; \ + TYPE der3 = er3; \ + TYPE dei3 = ei3; \ + TYPE der4 = er4; \ + TYPE dei4 = ei4; \ + TYPE complex x = cpack(dxr, dxi); \ + TYPE complex r = func(x); \ + TYPE rr = ADDSUFFIX(creal)(r); \ + TYPE ri = ADDSUFFIX(cimag)(r); \ + if (!((isequal(rr, der1) && isequal(ri, dei1)) || \ + (isequal(rr, der2) && isequal(ri, dei2)) || \ + (isequal(rr, der3) && isequal(ri, dei3)) || \ + (isequal(rr, der4) && isequal(ri, dei4)))) { \ + ret = 0; \ + TEST_PRINTF(func, dxr, dxi, der1, dei1, rr, ri); \ + printf("or"); \ + TEST_PRINTF(func, dxr, dxi, der2, dei2, rr, ri); \ + printf("or"); \ + TEST_PRINTF(func, dxr, dxi, der3, dei3, rr, ri); \ + printf("or"); \ + TEST_PRINTF(func, dxr, dxi, der4, dei4, rr, ri); \ + } \ + } \ + while(0) + +#define TEST_CPOW_INT(xr, xi, yr, yi, er, ei, test) \ + do { \ + TYPE dxr = xr; \ + TYPE dxi = xi; \ + TYPE dyr = yr; \ + TYPE dyi = yi; \ + TYPE der = er; \ + TYPE dei = ei; \ + TYPE complex x = cpack(xr, xi); \ + TYPE complex y = cpack(yr, yi); \ + TYPE complex r = ADDSUFFIX(cpow)(x, y); \ + TYPE rr = ADDSUFFIX(creal)(r); \ + TYPE ri = ADDSUFFIX(cimag)(r); \ + if (!(test(rr, der) && test(ri, dei))) { \ + ret = 0; \ + printf("%d: " STRINGIZE(cpow) STRINGIZE(SUFFIX) "(" FMT " + " FMT \ + "j, " FMT " + " FMT "j): expected: " FMT " + " FMT \ + "j: received: " FMT " + " FMT "j\n", __LINE__, dxr, dxi, \ + dyr, dyi, der, dei, rr, ri); \ + } \ + } \ + while(0) + +#define TEST_CPOW_EE(xr, xi, yr, yi, er, ei) \ + TEST_CPOW_INT(xr, xi, yr, yi, er, ei, isequal) + +#define TEST_CPOW_CC(xr, xi, yr, yi, er, ei) \ + TEST_CPOW_INT(xr, xi, yr, yi, er, ei, isclose) + +#define TEST_RAISES(func, xr, xi, er, ei, fpe) \ + do { \ + int except; \ + TYPE dxr = xr; \ + TYPE dxi = xi; \ + TYPE der = er; \ + TYPE dei = ei; \ + TYPE complex r; \ + TYPE complex x = cpack(xr, xi); \ + TYPE rr, ri; \ + feclearexcept(FE_ALL_EXCEPT); \ + r = ADDSUFFIX(func)(x); \ + except = fetestexcept(fpe); \ + rr = ADDSUFFIX(creal)(r); \ + ri = ADDSUFFIX(cimag)(r); \ + if (!(except & fpe && isequal(rr, der) && isequal(ri, dei))) { \ + ret = 0; \ + TEST_PRINTF(func, dxr, dxi, der, dei, rr, ri); \ + } \ + } \ + while(0) + +#define TEST_RAISES_UNSPECIFIED2(func, xr, xi, er1, ei1, er2, ei2, fpe) \ + do { \ + int except; \ + TYPE dxr = xr; \ + TYPE dxi = xi; \ + TYPE der1 = er1; \ + TYPE dei1 = ei1; \ + TYPE der2 = er2; \ + TYPE dei2 = ei2; \ + TYPE complex r; \ + TYPE complex x = cpack(xr, xi); \ + TYPE rr, ri; \ + feclearexcept(FE_ALL_EXCEPT); \ + r = ADDSUFFIX(func)(x); \ + except = fetestexcept(fpe); \ + rr = ADDSUFFIX(creal)(r); \ + ri = ADDSUFFIX(cimag)(r); \ + if (!(except & fpe && ((isequal(rr, der1) && isequal(ri, dei1)) \ + || (isequal(rr, der2) && isequal(ri, dei2))))) {\ + ret = 0; \ + TEST_PRINTF(func, dxr, dxi, der1, dei1, rr, ri); \ + printf("or"); \ + TEST_PRINTF(func, dxr, dxi, der2, dei2, rr, ri); \ + } \ + } \ + while(0) + +#define TEST_BRANCH_CUT(func, xr, xi, dxr, dxi, rsign, isign, cksignzero) \ + do { \ + TYPE vxr = xr; \ + TYPE vxi = xi; \ + TYPE vdxr = dxr; \ + TYPE vdxi = dxi; \ + int vrsign = rsign; \ + int visign = isign; \ + int vcksignzero = cksignzero; \ + TYPE complex x = cpack(vxr, vxi); \ + TYPE complex dx = cpack(vdxr, vdxi); \ + int q = check_branch_cut(ADDSUFFIX(func), x, dx, \ + vrsign, visign, vcksignzero); \ + if (!q) { \ + ret = 0; \ + printf(STRINGIZE(func) STRINGIZE(SUFFIX) ": branch cut failure: " \ + "x = " FMT " + " FMT "j, dx = " FMT " + " FMT "j, rsign = %d, " \ + "isign = %d, check_sign_zero = %d\n", vxr, vxi, \ + vdxr, vdxi, vrsign, visign, vcksignzero); \ + } \ + } \ + while(0) + +#define TEST_LOSS_OF_PRECISION(cfunc, rfunc, real) \ + do { \ + if (!check_loss_of_precision(ADDSUFFIX(cfunc), ADDSUFFIX(rfunc), real, \ + STRINGIZE(cfunc) STRINGIZE(SUFFIX))) { \ + ret = 0; \ + } \ + if (!check_near_crossover(ADDSUFFIX(cfunc), \ + STRINGIZE(cfunc) STRINGIZE(SUFFIX))) { \ + ret = 0; \ + } \ + } \ + while(0) + +TYPE complex cpack(TYPE r, TYPE i) +{ + union { + TYPE complex z; + TYPE a[2]; + } z1; + z1.a[0] = r; + z1.a[1] = i; + return z1.z; +} + +int isclose(TYPE a, TYPE b) +{ + const TYPE atol = CLOSE_ATOL; + const TYPE rtol = CLOSE_RTOL; + + if (isfinite(a) && isfinite(b)) { + return (ADDSUFFIX(fabs)(a - b) <= (atol + rtol*ADDSUFFIX(fabs)(b))); + } + return 0; +} + +int isequal(TYPE a, TYPE b) +{ + if (isfinite(a) && isfinite(b)) { + if (a == 0 && b == 0) { + TYPE signa = ADDSUFFIX(copysign)(1.0, a); + TYPE signb = ADDSUFFIX(copysign)(1.0, b); + return signa == signb; + } + else { + return a == b; + } + } + else if (isnan(a) && isnan(b)) { + return 1; + } + else {/* infs */ + return a == b; + } +} + +typedef TYPE complex (*complexfunc)(TYPE complex); +typedef TYPE (*realfunc)(TYPE); + +int check_branch_cut(complexfunc cfunc, TYPE complex x0, TYPE complex dx, + int re_sign, int im_sign, int sig_zero_ok) +{ + const TYPE scale = EPS * 1e3; + const TYPE atol = 1e-4; + + TYPE complex shift = dx*scale*ADDSUFFIX(cabs)(x0)/ADDSUFFIX(cabs)(dx); + TYPE complex y0 = cfunc(x0); + TYPE complex yp = cfunc(x0 + shift); + TYPE complex ym = cfunc(x0 - shift); + + TYPE y0r, y0i, ypr, ypi, ymr, ymi; + + y0r = ADDSUFFIX(creal)(y0); + y0i = ADDSUFFIX(cimag)(y0); + ypr = ADDSUFFIX(creal)(yp); + ypi = ADDSUFFIX(cimag)(yp); + ymr = ADDSUFFIX(creal)(ym); + ymi = ADDSUFFIX(cimag)(ym); + + if (ADDSUFFIX(fabs)(y0r - ypr) >= atol) + return 0; + if (ADDSUFFIX(fabs)(y0i - ypi) >= atol) + return 0; + if (ADDSUFFIX(fabs)(y0r - re_sign*ymr) >= atol) + return 0; + if (ADDSUFFIX(fabs)(y0i - im_sign*ymi) >= atol) + return 0; + + if (sig_zero_ok) { + if (ADDSUFFIX(creal)(x0) == 0 && ADDSUFFIX(creal)(dx) != 0) { + x0 = cpack(NZERO, ADDSUFFIX(cimag)(x0)); + ym = cfunc(x0); + + ymr = ADDSUFFIX(creal)(ym); + ymi = ADDSUFFIX(cimag)(ym); + if (ADDSUFFIX(fabs)(y0r - re_sign*ymr) >= atol) + return 0; + if (ADDSUFFIX(fabs)(y0i - im_sign*ymi) >= atol) + return 0; + } + else if (ADDSUFFIX(cimag)(x0) == 0 && ADDSUFFIX(cimag)(dx) != 0) { + x0 = cpack(ADDSUFFIX(creal)(x0), NZERO); + ym = cfunc(x0); + + ymr = ADDSUFFIX(creal)(ym); + ymi = ADDSUFFIX(cimag)(ym); + if (ADDSUFFIX(fabs)(y0r - re_sign*ymr) >= atol) + return 0; + if (ADDSUFFIX(fabs)(y0i - im_sign*ymi) >= atol) + return 0; + } + } + return 1; +} + +int check_near_crossover(complexfunc cfunc, const char* fname) +{ + const TYPE x = 1e-3; + const int rpnt[] = {-1, -1, -1, 0, 0, 1, 1, 1}; + const int ipnt[] = {-1, 0, 1, -1, 1, -1, 0, -1}; + const int npnt = sizeof(rpnt) / sizeof(int); + const int dr[] = {1, 0, 1}; + const int di[] = {0, 1, 1}; + const int ndr = sizeof(dr) / sizeof(int); + int k, j; + int ret = 1; + TYPE drj, dij, diff; + TYPE complex zp, zm, czp, czm; + + for (j = 0; j < ndr; j++) { + drj = 2 * x * dr[j] * EPS; + dij = 2 * x * di[j] * EPS; + for (k = 0; k < npnt; k++) { + zp = cpack(x*rpnt[k] + drj, x*ipnt[k] + dij); + zm = cpack(x*rpnt[k] - drj, x*ipnt[k] - dij); + + czp = cfunc(zp); + czm = cfunc(zm); + + diff = ADDSUFFIX(cabs)(czp - czm); + if ( diff > 2*EPS || czp == czm) { + printf(fname); + printf(": Loss of precision: j = %d, k = %d\n", j, k); + printf("zp = (" FMT " + " FMT "j) -> (" FMT " + " FMT "j)\n", \ + ADDSUFFIX(creal)(zp), ADDSUFFIX(cimag)(zp), \ + ADDSUFFIX(creal)(czp), ADDSUFFIX(cimag)(czp)); + printf("zm = (" FMT " + " FMT "j) -> (" FMT " + " FMT "j)\n", \ + ADDSUFFIX(creal)(zm), ADDSUFFIX(cimag)(zm), \ + ADDSUFFIX(creal)(czm), ADDSUFFIX(cimag)(czm)); + printf("diff = " FMT ", exact match = %d\n", diff, czp == czm); + ret = 0; + } + } + } + return ret; +} + +int clp_internal(complexfunc cfunc, realfunc rfunc, int real, TYPE x) +{ + TYPE num = rfunc(x); + TYPE den; + TYPE complex z; + + if (real == 1) { + z = cpack(x, 0); + z = cfunc(z); + den = ADDSUFFIX(creal)(z); + } + else { + z = cpack(0, x); + z = cfunc(z); + den = ADDSUFFIX(cimag)(z); + } + return ADDSUFFIX(fabs)(num/den - 1); +} + +int check_loss_of_precision(complexfunc cfunc, realfunc rfunc, int real, + const char* fname) +{ + const int n_series = 200; + const int n_basic = 10; + const TYPE rtol = 2*EPS; + + const TYPE xsb = -20; + const TYPE xse = -3.001; + const TYPE dxs = (xse - xsb) / n_series; + + const TYPE xbb = -2.999; + const TYPE xbe = 0; + const TYPE dxb = (xbe - xbb) / n_basic; + + TYPE x, ratio; + int k; + int ret = 1; + + for(k = 0; k < n_series; k++) { + x = ADDSUFFIX(pow)(10.0, xsb + k*dxs); + ratio = clp_internal(cfunc, rfunc, real, x); + if (ratio > rtol) { + printf(fname); + printf(": Loss of precision vs real:\n"); + printf("x = " FMT "\n", x); + printf("ratio = " FMT "\n", ratio); + ret = 0; + } + } + + for(k = 0; k < n_basic; k++) { + x = ADDSUFFIX(pow)(10.0, xbb + k*dxb); + ratio = clp_internal(cfunc, rfunc, real, x); + if (ratio > rtol) { + printf(fname); + printf(": Loss of precision vs. real:\n"); + printf("x = " FMT "\n", x); + printf("ratio = " FMT "\n", ratio); + ret = 0; + } + } + return ret; +} + +#ifdef CACOS +int test_cacos() +{ + int ret = 1; + /* cacos(conj(z)) = conj(cacos(z)) */ + TEST_CE(cacos, 0, 0, NPY_PI_2, NZERO); + TEST_CE(cacos, 0, NZERO, NPY_PI_2, 0); + + TEST_CE(cacos, NZERO, 0, NPY_PI_2, NZERO); + TEST_CE(cacos, NZERO, NZERO, NPY_PI_2, 0); + + TEST_CE(cacos, 0, NAN, NPY_PI_2, NAN); + TEST_CE(cacos, NZERO, NAN, NPY_PI_2, NAN); + + TEST_CE(cacos, 2.0, INFINITY, NPY_PI_2, -INFINITY); + TEST_CE(cacos, 2.0, -INFINITY, NPY_PI_2, INFINITY); + + /* can raise FE_INVALID or not */ + TEST_EE(cacos, 2.0, NAN, NAN, NAN); + + TEST_CE(cacos, -INFINITY, 2.0, NPY_PI, -INFINITY); + TEST_CE(cacos, -INFINITY, -2.0, NPY_PI, INFINITY); + + TEST_EE(cacos, INFINITY, 2.0, 0, -INFINITY); + TEST_EE(cacos, INFINITY, -2.0, 0, INFINITY); + + TEST_CE(cacos, -INFINITY, INFINITY, 0.75 * NPY_PI, -INFINITY); + TEST_CE(cacos, -INFINITY, -INFINITY, 0.75 * NPY_PI, INFINITY); + + TEST_CE(cacos, INFINITY, INFINITY, 0.25 * NPY_PI, -INFINITY); + TEST_CE(cacos, INFINITY, -INFINITY, 0.25 * NPY_PI, -INFINITY); + + /* sign of imaginary part is unspecified. */ + TEST_UNSPECIFIED2(cacos, INFINITY, NAN, NAN, INFINITY, NAN, -INFINITY); + TEST_UNSPECIFIED2(cacos, -INFINITY, NAN, NAN, INFINITY, NAN, -INFINITY); + + /* can raise FE_INVALID or not */ + TEST_EE(cacos, NAN, 2.0, NAN, NAN); + TEST_EE(cacos, NAN, -2.0, NAN, NAN); + + TEST_EE(cacos, NAN, INFINITY, NAN, -INFINITY); + TEST_EE(cacos, NAN, -INFINITY, NAN, INFINITY); + + TEST_EE(cacos, NAN, NAN, NAN, NAN); + + TEST_BRANCH_CUT(cacos, -2, 0, 0, 1, 1, -1, 1); + TEST_BRANCH_CUT(cacos, 2, 0, 0, -1, 1, -1, 1); + TEST_BRANCH_CUT(cacos, 0, -2, 1, 0, 1, 1, 1); + TEST_BRANCH_CUT(cacos, 0, 2, 1, 0, 1, 1, 1); + + TEST_CC(cacos, 0.5, 0.0, ADDSUFFIX(acos)(0.5), 0.0); + + return ret; +} +#endif + +#ifdef CASIN +int test_casin() +{ + int ret = 1; + + /* casin(conj(z)) = conj(casin(z)) and casin is odd */ + TEST_EE(casin, 0, 0, 0, 0); + TEST_EE(casin, 0, NZERO, 0, NZERO); + TEST_EE(casin, NZERO, 0, NZERO, 0); + TEST_EE(casin, NZERO, NZERO, NZERO, NZERO); + + TEST_CE(casin, -INFINITY, 2.0, -NPY_PI_2, INFINITY); + TEST_CE(casin, INFINITY, 2.0, NPY_PI_2, INFINITY); + TEST_CE(casin, -INFINITY, -2.0, -NPY_PI_2, -INFINITY); + TEST_CE(casin, INFINITY, -2.0, NPY_PI_2, -INFINITY); + + /* can raise FE_INVALID or not */ + TEST_EE(casin, NAN, -2.0, NAN, NAN); + TEST_EE(casin, NAN, 2.0, NAN, NAN); + + TEST_EE(casin, -2.0, INFINITY, NZERO, INFINITY); + TEST_EE(casin, 2.0, INFINITY, 0, INFINITY); + TEST_EE(casin, -2.0, -INFINITY, NZERO, -INFINITY); + TEST_EE(casin, 2.0, -INFINITY, 0, -INFINITY); + + TEST_CE(casin, -INFINITY, INFINITY, -0.25*NPY_PI, INFINITY); + TEST_CE(casin, INFINITY, INFINITY, 0.25*NPY_PI, INFINITY); + TEST_CE(casin, -INFINITY, -INFINITY, -0.25*NPY_PI, -INFINITY); + TEST_CE(casin, INFINITY, -INFINITY, 0.25*NPY_PI, -INFINITY); + + TEST_EE(casin, NAN, INFINITY, NAN, INFINITY); + TEST_EE(casin, NAN, -INFINITY, NAN, -INFINITY); + + TEST_EE(casin, 0, NAN, 0, NAN); + TEST_EE(casin, NZERO, NAN, NZERO, NAN); + + /* can raise FE_INVALID or not */ + TEST_EE(casin, -2.0, NAN, NAN, NAN); + TEST_EE(casin, 2.0, NAN, NAN, NAN); + + /* sign of real part is unspecified */ + TEST_UNSPECIFIED2(casin, -INFINITY, NAN, NAN, INFINITY, NAN, -INFINITY); + TEST_UNSPECIFIED2(casin, INFINITY, NAN, NAN, INFINITY, NAN, -INFINITY); + + TEST_EE(casin, NAN, NAN, NAN, NAN); + + TEST_LOSS_OF_PRECISION(casin, asin, 0); + + TEST_CC(casin, 1e-5, 1e-5, 9.999999999666666667e-6, 1.0000000000333333333e-5); + + TEST_BRANCH_CUT(casin, -2, 0, 0, 1, 1, -1, 1); + TEST_BRANCH_CUT(casin, 2, 0, 0, -1, 1, -1, 1); + TEST_BRANCH_CUT(casin, 0, -2, 1, 0, 1, 1, 1); + TEST_BRANCH_CUT(casin, 0, 2, 1, 0, 1, 1, 1); + + TEST_CC(casin, 0.5, 0, ADDSUFFIX(asin)(0.5), 0); + + return ret; +} +#endif + +#ifdef CATAN +int test_catan() +{ + int ret = 1; + /* catan(conj(z)) = conj(catan(z)) and catan is odd */ + TEST_EE(catan, 0, 0, 0, 0); + TEST_EE(catan, 0, NZERO, 0, NZERO); + TEST_EE(catan, NZERO, 0, NZERO, 0); + TEST_EE(catan, NZERO, NZERO, NZERO, NZERO); + + TEST_EE(catan, NAN, 0, NAN, 0); + TEST_EE(catan, NAN, NZERO, NAN, NZERO); + + TEST_RAISES(catan, NZERO, 1, NZERO, INFINITY, FE_DIVBYZERO); + TEST_RAISES(catan, 0, 1, 0, INFINITY, FE_DIVBYZERO); + TEST_RAISES(catan, NZERO, -1, NZERO, -INFINITY, FE_DIVBYZERO); + TEST_RAISES(catan, 0, -1, 0, -INFINITY, FE_DIVBYZERO); + + TEST_CE(catan, -INFINITY, 2.0, -NPY_PI_2, 0); + TEST_CE(catan, INFINITY, 2.0, NPY_PI_2, 0); + TEST_CE(catan, -INFINITY, -2.0, -NPY_PI_2, NZERO); + TEST_CE(catan, INFINITY, -2.0, NPY_PI_2, NZERO); + + /* can raise FE_INVALID or not */ + TEST_EE(catan, NAN, -2.0, NAN, NAN); + TEST_EE(catan, NAN, 2.0, NAN, NAN); + + TEST_CE(catan, -2.0, INFINITY, -NPY_PI_2, 0); + TEST_CE(catan, 2.0, INFINITY, NPY_PI_2, 0); + TEST_CE(catan, -2.0, -INFINITY, -NPY_PI_2, NZERO); + TEST_CE(catan, 2.0, -INFINITY, NPY_PI_2, NZERO); + + TEST_CE(catan, -INFINITY, INFINITY, -NPY_PI_2, 0); + TEST_CE(catan, INFINITY, INFINITY, NPY_PI_2, 0); + TEST_CE(catan, -INFINITY, -INFINITY, -NPY_PI_2, NZERO); + TEST_CE(catan, INFINITY, -INFINITY, NPY_PI_2, NZERO); + + TEST_EE(catan, NAN, INFINITY, NAN, 0); + TEST_EE(catan, NAN, -INFINITY, NAN, NZERO); + + /* can raise FE_INVALID or not */ + TEST_EE(catan, -2.0, NAN, NAN, NAN); + TEST_EE(catan, 2.0, NAN, NAN, NAN); + + /* sign of real part is unspecified */ + TEST_UNSPECIFIED2(catan, -INFINITY, NAN, -NPY_PI_2, 0, -NPY_PI_2, NZERO); + TEST_UNSPECIFIED2(catan, INFINITY, NAN, NPY_PI_2, 0, NPY_PI_2, NZERO); + + TEST_EE(catan, NAN, NAN, NAN, NAN); + + TEST_LOSS_OF_PRECISION(catan, atan, 0); + + TEST_CC(catan, 1e-5, 1e-5, 1.000000000066666666e-5, 9.999999999333333333e-6); + + TEST_BRANCH_CUT(catan, 0, -2, 1, 0, -1, 1, 1); + TEST_BRANCH_CUT(catan, 0, 2, -1, 0, -1, 1, 1); + TEST_BRANCH_CUT(catan, -2, 0, 0, 1, 1, 1, 1); + TEST_BRANCH_CUT(catan, 2, 0, 0, 1, 1, 1, 1); + + TEST_CC(catan, 0.5, 0, ADDSUFFIX(catan)(0.5), 0); + + return ret; +} +#endif + +#ifdef CACOSH +int test_cacosh() +{ + int ret = 1; + /* cacosh(conj(z)) = conj(cacosh(z)) */ + TEST_EC(cacosh, 0, 0, 0, NPY_PI_2); + TEST_EC(cacosh, 0, NZERO, 0, -NPY_PI_2); + + TEST_EC(cacosh, NZERO, 0, 0, NPY_PI_2); + TEST_EC(cacosh, NZERO, NZERO, 0, -NPY_PI_2); + + TEST_EC(cacosh, 2.0, INFINITY, INFINITY, NPY_PI_2); + TEST_EC(cacosh, 2.0, -INFINITY, INFINITY, -NPY_PI_2); + + /* can raise FE_INVALID or not */ + TEST_EE(cacosh, 2.0, NAN, NAN, NAN); + + TEST_EC(cacosh, -INFINITY, 2.0, INFINITY, NPY_PI); + TEST_EC(cacosh, -INFINITY, -2.0, INFINITY, -NPY_PI); + + TEST_EE(cacosh, INFINITY, 2.0, INFINITY, 0); + TEST_EE(cacosh, INFINITY, -2.0, INFINITY, NZERO); + + TEST_EC(cacosh, -INFINITY, INFINITY, INFINITY, 0.75*NPY_PI); + TEST_EC(cacosh, -INFINITY, -INFINITY, INFINITY, -0.75*NPY_PI); + + TEST_EC(cacosh, INFINITY, INFINITY, INFINITY, 0.25*NPY_PI); + TEST_EC(cacosh, INFINITY, -INFINITY, INFINITY, -0.25*NPY_PI); + + TEST_EE(cacosh, INFINITY, NAN, INFINITY, NAN); + TEST_EE(cacosh, -INFINITY, NAN, INFINITY, NAN); + + /* can raise FE_INVALID or not */ + TEST_EE(cacosh, NAN, 2.0, NAN, NAN); + TEST_EE(cacosh, NAN, -2.0, NAN, NAN); + + TEST_EE(cacosh, NAN, INFINITY, INFINITY, NAN); + TEST_EE(cacosh, NAN, -INFINITY, INFINITY, NAN); + + TEST_EE(cacosh, NAN, NAN, NAN, NAN); + + TEST_BRANCH_CUT(cacosh, -1, 0, 0, 1, 1, -1, 1); + TEST_BRANCH_CUT(cacosh, 0.5, 0, 0, 1, 1, -1, 1); + TEST_BRANCH_CUT(cacosh, 0, -2, 1, 0, 1, 1, 1); + TEST_BRANCH_CUT(cacosh, 0, 2, 1, 0, 1, 1, 1); + TEST_BRANCH_CUT(cacosh, 2, 0, 0, 1, 1, 1, 1); + + TEST_CC(cacosh, 1.5, 0, ADDSUFFIX(acosh)(1.5), 0); + return ret; +} +#endif + +#ifdef CASINH +int test_casinh() +{ + int ret = 1; + /* casinh(conj(z)) = conj(casinh(z)) and casinh is odd */ + TEST_EE(casinh, 0, 0, 0, 0); + TEST_EE(casinh, 0, NZERO, 0, NZERO); + TEST_EE(casinh, NZERO, 0, NZERO, 0); + TEST_EE(casinh, NZERO, NZERO, NZERO, NZERO); + + TEST_EC(casinh, 2.0, INFINITY, INFINITY, NPY_PI_2); + TEST_EC(casinh, 2.0, -INFINITY, INFINITY, -NPY_PI_2); + TEST_EC(casinh, -2.0, INFINITY, -INFINITY, NPY_PI_2); + TEST_EC(casinh, -2.0, -INFINITY, -INFINITY, -NPY_PI_2); + + /* can raise FE_INVALID or not */ + TEST_EE(casinh, 2.0, NAN, NAN, NAN); + TEST_EE(casinh, -2.0, NAN, NAN, NAN); + + TEST_EE(casinh, INFINITY, 2.0, INFINITY, 0); + TEST_EE(casinh, INFINITY, -2.0, INFINITY, NZERO); + TEST_EE(casinh, -INFINITY, 2.0, -INFINITY, 0); + TEST_EE(casinh, -INFINITY, -2.0, -INFINITY, NZERO); + + TEST_EC(casinh, INFINITY, INFINITY, INFINITY, 0.25*NPY_PI); + TEST_EC(casinh, INFINITY, -INFINITY, INFINITY, -0.25*NPY_PI); + TEST_EC(casinh, -INFINITY, INFINITY, -INFINITY, 0.25*NPY_PI); + TEST_EC(casinh, -INFINITY, -INFINITY, -INFINITY, -0.25*NPY_PI); + + TEST_EE(casinh, INFINITY, NAN, INFINITY, NAN); + TEST_EE(casinh, -INFINITY, NAN, -INFINITY, NAN); + + TEST_EE(casinh, NAN, 0, NAN, 0); + TEST_EE(casinh, NAN, NZERO, NAN, NZERO); + + /* can raise FE_INVALID or not */ + TEST_EE(casinh, NAN, 2.0, NAN, NAN); + TEST_EE(casinh, NAN, -2.0, NAN, NAN); + + /* sign of real part is unspecified */ + TEST_UNSPECIFIED2(casinh, NAN, INFINITY, INFINITY, NAN, -INFINITY, NAN); + TEST_UNSPECIFIED2(casinh, NAN, -INFINITY, INFINITY, NAN, -INFINITY, NAN); + + TEST_EE(casinh, NAN, NAN, NAN, NAN); + + TEST_LOSS_OF_PRECISION(casinh, asinh, 1); + + TEST_CC(casinh, 1e-5, 1e-5, 1.0000000000333333333e-5, 9.999999999666666667e-6); + + TEST_BRANCH_CUT(casinh, 0, -2, -1, 0, -1, 1, 1); + TEST_BRANCH_CUT(casinh, 0, 2, 1, 0, -1, 1, 1); + TEST_BRANCH_CUT(casinh, -2, 0, 0, 1, 1, 1, 1); + TEST_BRANCH_CUT(casinh, 2, 0, 0, 1, 1, 1, 1); + TEST_BRANCH_CUT(casinh, 0, 0, 1, 0, 1, 1, 1); + + TEST_CC(casinh, 0.5, 0, ADDSUFFIX(asinh)(0.5), 0); + + return ret; +} +#endif + +#ifdef CATANH +int test_catanh() +{ + int ret = 1; + /* catanh(conj(z)) = conj(catanh(z)) and catanh is odd */ + TEST_EE(catanh, 0, 0, 0, 0); + TEST_EE(catanh, 0, NZERO, 0, NZERO); + TEST_EE(catanh, NZERO, 0, NZERO, 0); + TEST_EE(catanh, NZERO, NZERO, NZERO, NZERO); + + TEST_EE(catanh, 0, NAN, 0, NAN); + TEST_EE(catanh, NZERO, NAN, NZERO, NAN); + + TEST_RAISES(catanh, 1, 0, INFINITY, 0, FE_DIVBYZERO); + TEST_RAISES(catanh, 1, NZERO, INFINITY, NZERO, FE_DIVBYZERO); + TEST_RAISES(catanh, -1, 0, -INFINITY, 0, FE_DIVBYZERO); + TEST_RAISES(catanh, -1, NZERO, -INFINITY, NZERO, FE_DIVBYZERO); + + TEST_EC(catanh, 2.0, INFINITY, 0, NPY_PI_2); + TEST_EC(catanh, 2.0, -INFINITY, 0, -NPY_PI_2); + TEST_EC(catanh, -2.0, INFINITY, NZERO, NPY_PI_2); + TEST_EC(catanh, -2.0, -INFINITY, NZERO, -NPY_PI_2); + + /* can raise FE_INVALID or not */ + TEST_EE(catanh, 2.0, NAN, NAN, NAN); + TEST_EE(catanh, -2.0, NAN, NAN, NAN); + + TEST_EC(catanh, INFINITY, 2.0, 0, NPY_PI_2); + TEST_EC(catanh, INFINITY, -2.0, 0, -NPY_PI_2); + TEST_EC(catanh, -INFINITY, 2.0, NZERO, NPY_PI_2); + TEST_EC(catanh, -INFINITY, -2.0, NZERO, -NPY_PI_2); + + TEST_EC(catanh, INFINITY, INFINITY, 0, NPY_PI_2); + TEST_EC(catanh, INFINITY, -INFINITY, 0, -NPY_PI_2); + TEST_EC(catanh, -INFINITY, INFINITY, NZERO, NPY_PI_2); + TEST_EC(catanh, -INFINITY, -INFINITY, NZERO, -NPY_PI_2); + + TEST_EE(catanh, INFINITY, NAN, 0, NAN); + TEST_EE(catanh, -INFINITY, NAN, NZERO, NAN); + + /* can raise FE_INVALID or not */ + TEST_EE(catanh, NAN, 2.0, NAN, NAN); + TEST_EE(catanh, NAN, -2.0, NAN, NAN); + + /* sign of real part is unspecified */ + TEST_UNSPECIFIED2(catanh, NAN, INFINITY, 0, NPY_PI_2, NZERO, NPY_PI_2); + TEST_UNSPECIFIED2(catanh, NAN, -INFINITY, 0, -NPY_PI_2, NZERO, -NPY_PI_2); + + /* TEST(catanh, NAN, INFINITY, 0, NPY_PI_2); */ + TEST_EE(catanh, NAN, NAN, NAN, NAN); + + TEST_LOSS_OF_PRECISION(catanh, atanh, 1); + + TEST_CC(catanh, 1e-5, 1e-5, 9.999999999333333333e-6, 1.000000000066666666e-5); + + TEST_BRANCH_CUT(catanh, -2, 0, 0, 1, 1, -1, 1); + TEST_BRANCH_CUT(catanh, 2, 0, 0, -1, 1, -1, 1); + TEST_BRANCH_CUT(catanh, 0, -2, 1, 0, 1, 1, 1); + TEST_BRANCH_CUT(catanh, 0, 2, 1, 0, 1, 1, 1); + TEST_BRANCH_CUT(catanh, 0, 0, 0, 1, 1, 1, 1); + + TEST_CC(catanh, 0.5, 0, ADDSUFFIX(atanh)(0.5), 0); + + return ret; +} +#endif + +#ifdef CCOS +int test_ccos() +{ + int ret = 1; + /* ccos(conj(z)) = conj(ccos(z)) and ccos is even */ + TEST_EE(ccos, NZERO, 0, 1, 0); + TEST_EE(ccos, 0, 0, 1, NZERO); + TEST_EE(ccos, NZERO, NZERO, 1, NZERO); + TEST_EE(ccos, 0, NZERO, 1, 0); + + /* sign of imaginary part is unspecified */ + TEST_RAISES_UNSPECIFIED2(ccos, -INFINITY, 0, NAN, 0, \ + NAN, NZERO, FE_INVALID); + + /* sign of imaginary part is unspecified */ + TEST_UNSPECIFIED2(ccos, NAN, 0, NAN, 0, NAN, NZERO); + TEST_UNSPECIFIED2(ccos, NAN, NZERO, NAN, 0, NAN, NZERO); + + TEST_RAISES(ccos, -INFINITY, 2.0, NAN, NAN, FE_INVALID); + TEST_RAISES(ccos, INFINITY, 2.0, NAN, NAN, FE_INVALID); + TEST_RAISES(ccos, -INFINITY, -2.0, NAN, NAN, FE_INVALID); + TEST_RAISES(ccos, INFINITY, -2.0, NAN, NAN, FE_INVALID); + + /* can raise FE_INVALID or not */ + TEST_EE(ccos, NAN, 2.0, NAN, NAN); + TEST_EE(ccos, NAN, -2.0, NAN, NAN); + + TEST_EE(ccos, NZERO, INFINITY, INFINITY, 0); + TEST_EE(ccos, 0, INFINITY, INFINITY, NZERO); + TEST_EE(ccos, NZERO, -INFINITY, INFINITY, NZERO); + TEST_EE(ccos, 0, -INFINITY, INFINITY, 0); + + TEST_EE(ccos, -1.0, INFINITY, INFINITY, INFINITY); + TEST_EE(ccos, 1.0, INFINITY, INFINITY, -INFINITY); + TEST_EE(ccos, -1.0, -INFINITY, INFINITY, -INFINITY); + TEST_EE(ccos, 1.0, -INFINITY, INFINITY, INFINITY); + TEST_EE(ccos, -2.0, INFINITY, -INFINITY, INFINITY); + TEST_EE(ccos, 2.0, INFINITY, -INFINITY, -INFINITY); + TEST_EE(ccos, -2.0, -INFINITY, -INFINITY, -INFINITY); + TEST_EE(ccos, 2.0, -INFINITY, -INFINITY, INFINITY); + TEST_EE(ccos, -4.0, INFINITY, -INFINITY, -INFINITY); + TEST_EE(ccos, 4.0, INFINITY, -INFINITY, INFINITY); + TEST_EE(ccos, -4.0, -INFINITY, -INFINITY, INFINITY); + TEST_EE(ccos, 4.0, -INFINITY, -INFINITY, -INFINITY); + TEST_EE(ccos, -5.0, INFINITY, INFINITY, -INFINITY); + TEST_EE(ccos, 5.0, INFINITY, INFINITY, INFINITY); + TEST_EE(ccos, -5.0, -INFINITY, INFINITY, INFINITY); + TEST_EE(ccos, 5.0, -INFINITY, INFINITY, -INFINITY); + + /* sign of real part is unspecified */ + TEST_RAISES_UNSPECIFIED2(ccos, -INFINITY, INFINITY, INFINITY, NAN, \ + -INFINITY, NAN, FE_INVALID); + + TEST_EE(ccos, NAN, INFINITY, INFINITY, NAN); + TEST_EE(ccos, NAN, -INFINITY, INFINITY, NAN); + + /* sign of imaginary part is unspecified */ + TEST_UNSPECIFIED2(ccos, 0, NAN, NAN, 0, NAN, NZERO); + TEST_UNSPECIFIED2(ccos, NZERO, NAN, NAN, 0, NAN, NZERO); + + /* can raise FE_INVALID or not */ + TEST_EE(ccos, -2.0, NAN, NAN, NAN); + TEST_EE(ccos, 2.0, NAN, NAN, NAN); + + TEST_EE(ccos, NAN, NAN, NAN, NAN); + + TEST_CC(ccos, 0.5, 0, ADDSUFFIX(cos)(0.5), 0); + + return ret; +} +#endif + +#ifdef CSIN +int test_csin() +{ + int ret = 1; + /* csin(conj(z)) = conj(csin(z)) and csin is odd */ + TEST_EE(csin, 0, 0, 0, 0); + TEST_EE(csin, 0, NZERO, 0, NZERO); + TEST_EE(csin, NZERO, 0, NZERO, 0); + TEST_EE(csin, NZERO, NZERO, NZERO, NZERO); + + /* sign of imaginary part is unspecified */ + TEST_RAISES_UNSPECIFIED2(csin, -INFINITY, 0, NAN, 0, \ + NAN, NZERO, FE_INVALID); + + /* sign of imaginary part is unspecified */ + TEST_UNSPECIFIED2(csin, NAN, 0, NAN, 0, NAN, NZERO); + TEST_UNSPECIFIED2(csin, NAN, NZERO, NAN, 0, NAN, NZERO); + + TEST_RAISES(csin, -INFINITY, 2.0, NAN, NAN, FE_INVALID); + TEST_RAISES(csin, INFINITY, 2.0, NAN, NAN, FE_INVALID); + TEST_RAISES(csin, -INFINITY, -2.0, NAN, NAN, FE_INVALID); + TEST_RAISES(csin, INFINITY, -2.0, NAN, NAN, FE_INVALID); + + /* can raise FE_INVALID or not */ + TEST_EE(csin, NAN, 2.0, NAN, NAN); + TEST_EE(csin, NAN, -2.0, NAN, NAN); + + TEST_EE(csin, NZERO, INFINITY, NZERO, INFINITY); + TEST_EE(csin, 0, INFINITY, 0, INFINITY); + TEST_EE(csin, NZERO, -INFINITY, NZERO, -INFINITY); + TEST_EE(csin, 0, -INFINITY, 0, -INFINITY); + + TEST_EE(csin, -1.0, INFINITY, -INFINITY, INFINITY); + TEST_EE(csin, 1.0, INFINITY, INFINITY, INFINITY); + TEST_EE(csin, -1.0, -INFINITY, -INFINITY, -INFINITY); + TEST_EE(csin, 1.0, -INFINITY, INFINITY, -INFINITY); + TEST_EE(csin, -2.0, INFINITY, -INFINITY, -INFINITY); + TEST_EE(csin, 2.0, INFINITY, INFINITY, -INFINITY); + TEST_EE(csin, -2.0, -INFINITY, -INFINITY, INFINITY); + TEST_EE(csin, 2.0, -INFINITY, INFINITY, INFINITY); + TEST_EE(csin, -4.0, INFINITY, INFINITY, -INFINITY); + TEST_EE(csin, 4.0, INFINITY, -INFINITY, -INFINITY); + TEST_EE(csin, -4.0, -INFINITY, INFINITY, INFINITY); + TEST_EE(csin, 4.0, -INFINITY, -INFINITY, INFINITY); + TEST_EE(csin, -5.0, INFINITY, INFINITY, INFINITY); + TEST_EE(csin, 5.0, INFINITY, -INFINITY, INFINITY); + TEST_EE(csin, -5.0, -INFINITY, INFINITY, -INFINITY); + TEST_EE(csin, 5.0, -INFINITY, -INFINITY, -INFINITY); + + /* sign of imaginary part is unspecified */ + TEST_RAISES_UNSPECIFIED2(csin, -INFINITY, INFINITY, NAN, INFINITY, \ + NAN, -INFINITY, FE_INVALID); + + /* sign of imaginary part is unspecified */ + TEST_UNSPECIFIED2(csin, NAN, INFINITY, NAN, INFINITY, NAN, -INFINITY); + TEST_UNSPECIFIED2(csin, NAN, -INFINITY, NAN, INFINITY, NAN, -INFINITY); + + TEST_EE(csin, 0, NAN, 0, NAN); + TEST_EE(csin, NZERO, NAN, NZERO, NAN); + + /* can raise FE_INVALID or not */ + TEST_EE(csin, -2.0, NAN, NAN, NAN); + TEST_EE(csin, 2.0, NAN, NAN, NAN); + + TEST_EE(csin, NAN, NAN, NAN, NAN); + + TEST_CC(csin, 0.5, 0, ADDSUFFIX(sin)(0.5), 0); + + return ret; +} +#endif + +#ifdef CTAN +int test_ctan() +{ + int ret = 1; + /* ctan(conj(z)) = conj(ctan(z)) and ctan is odd */ + TEST_EE(ctan, 0, 0, 0, 0); + TEST_EE(ctan, 0, NZERO, 0, NZERO); + TEST_EE(ctan, NZERO, 0, NZERO, 0); + TEST_EE(ctan, NZERO, NZERO, NZERO, NZERO); + + TEST_RAISES(ctan, -INFINITY, 2.0, NAN, NAN, FE_INVALID); + TEST_RAISES(ctan, -INFINITY, -2.0, NAN, NAN, FE_INVALID); + + /* can raise FE_INVALID or not */ + TEST_EE(ctan, NAN, 2.0, NAN, NAN); + TEST_EE(ctan, NAN, -2.0, NAN, NAN); + + TEST_EE(ctan, -1.0, INFINITY, NZERO, 1.0); + TEST_EE(ctan, 1.0, INFINITY, 0, 1.0); + TEST_EE(ctan, -1.0, -INFINITY, NZERO, -1.0); + TEST_EE(ctan, 1.0, -INFINITY, 0, -1.0); + TEST_EE(ctan, -2.0, INFINITY, 0, 1); + TEST_EE(ctan, 2.0, INFINITY, NZERO, 1); + TEST_EE(ctan, -2.0, -INFINITY, 0, -1); + TEST_EE(ctan, 2.0, -INFINITY, NZERO, -1); + + /* sign of real part is unspecified */ + TEST_UNSPECIFIED2(ctan, INFINITY, INFINITY, 0, 1, NZERO, 1); + TEST_UNSPECIFIED2(ctan, -INFINITY, INFINITY, 0, 1, NZERO, 1); + TEST_UNSPECIFIED2(ctan, INFINITY, -INFINITY, 0, -1, NZERO, -1); + TEST_UNSPECIFIED2(ctan, -INFINITY, -INFINITY, 0, -1, NZERO, -1); + + /* sign of real part is unspecified */ + TEST_UNSPECIFIED2(ctan, NAN, INFINITY, 0, 1, NZERO, 1); + TEST_UNSPECIFIED2(ctan, NAN, -INFINITY, 0, -1, NZERO, -1); + + TEST_EE(ctan, 0, NAN, 0, NAN); + TEST_EE(ctan, NZERO, NAN, NZERO, NAN); + + /* can raise FE_INVALID or not */ + TEST_EE(ctan, 2.0, NAN, NAN, NAN); + TEST_EE(ctan, -2.0, NAN, NAN, NAN); + + TEST_EE(ctan, NAN, NAN, NAN, NAN); + + TEST_CC(ctan, 0.5, 0, ADDSUFFIX(tan)(0.5), 0); + + TEST_CC(ctan, 0, 1000, 0, 1); + TEST_CC(ctan, 0, -1000, 0, -1); + + return ret; +} +#endif + +#ifdef CCOSH +int test_ccosh() +{ + int ret = 1; + /* ccosh(conj(z)) = conj(ccosh(z)) and ccosh is even */ + TEST_EE(ccosh, 0, 0, 1, 0); + TEST_EE(ccosh, 0, NZERO, 1, NZERO); + TEST_EE(ccosh, NZERO, 0, 1, NZERO); + TEST_EE(ccosh, NZERO, NZERO, 1, 0); + + /* sign of imaginary part is unspecified */ + TEST_RAISES_UNSPECIFIED2(ccosh, 0, INFINITY, NAN, 0, \ + NAN, NZERO, FE_INVALID); + + /* sign of imaginary part is unspecified */ + TEST_UNSPECIFIED2(ccosh, 0, NAN, NAN, 0, NAN, NZERO); + TEST_UNSPECIFIED2(ccosh, NZERO, NAN, NAN, 0, NAN, NZERO); + + TEST_RAISES(ccosh, 2.0, INFINITY, NAN, NAN, FE_INVALID); + TEST_RAISES(ccosh, 2.0, -INFINITY, NAN, NAN, FE_INVALID); + TEST_RAISES(ccosh, -2.0, INFINITY, NAN, NAN, FE_INVALID); + TEST_RAISES(ccosh, -2.0, -INFINITY, NAN, NAN, FE_INVALID); + + /* can raise FE_INVALID or not */ + TEST_EE(ccosh, 2.0, NAN, NAN, NAN); + TEST_EE(ccosh, -2.0, NAN, NAN, NAN); + + TEST_EE(ccosh, INFINITY, 0, INFINITY, 0); + TEST_EE(ccosh, INFINITY, NZERO, INFINITY, NZERO); + TEST_EE(ccosh, -INFINITY, 0, INFINITY, NZERO); + TEST_EE(ccosh, -INFINITY, NZERO, INFINITY, 0); + + TEST_EE(ccosh, INFINITY, 1.0, INFINITY, INFINITY); + TEST_EE(ccosh, INFINITY, -1.0, INFINITY, -INFINITY); + TEST_EE(ccosh, -INFINITY, 1.0, INFINITY, -INFINITY); + TEST_EE(ccosh, -INFINITY, -1.0, INFINITY, INFINITY); + TEST_EE(ccosh, INFINITY, 2.0, -INFINITY, INFINITY); + TEST_EE(ccosh, INFINITY, -2.0, -INFINITY, -INFINITY); + TEST_EE(ccosh, -INFINITY, 2.0, -INFINITY, -INFINITY); + TEST_EE(ccosh, -INFINITY, -2.0, -INFINITY, INFINITY); + TEST_EE(ccosh, INFINITY, 4.0, -INFINITY, -INFINITY); + TEST_EE(ccosh, INFINITY, -4.0, -INFINITY, INFINITY); + TEST_EE(ccosh, -INFINITY, 4.0, -INFINITY, INFINITY); + TEST_EE(ccosh, -INFINITY, -4.0, -INFINITY, -INFINITY); + TEST_EE(ccosh, INFINITY, 5.0, INFINITY, -INFINITY); + TEST_EE(ccosh, INFINITY, -5.0, INFINITY, INFINITY); + TEST_EE(ccosh, -INFINITY, 5.0, INFINITY, INFINITY); + TEST_EE(ccosh, -INFINITY, -5.0, INFINITY, -INFINITY); + + /* sign of real part is unspecified */ + TEST_RAISES_UNSPECIFIED2(ccosh, INFINITY, INFINITY, INFINITY, NAN, \ + -INFINITY, NAN, FE_INVALID); + + TEST_EE(ccosh, INFINITY, NAN, INFINITY, NAN); + TEST_EE(ccosh, -INFINITY, NAN, INFINITY, NAN); + + /* sign of imaginary part is unspecified */ + TEST_UNSPECIFIED2(ccosh, NAN, 0, NAN, 0, NAN, NZERO); + TEST_UNSPECIFIED2(ccosh, NAN, NZERO, NAN, 0, NAN, NZERO); + + /* can raise FE_INVALID or not */ + TEST_EE(ccosh, NAN, 2.0, NAN, NAN); + TEST_EE(ccosh, NAN, -2.0, NAN, NAN); + + TEST_EE(ccosh, NAN, NAN, NAN, NAN); + + TEST_CC(ccosh, 0.5, 0, ADDSUFFIX(cosh)(0.5), 0); + + return ret; +} +#endif + +#ifdef CSINH +int test_csinh() +{ + int ret = 1; + /* csinh(conj(z)) = conj(csinh(z)) and csinh is odd */ + TEST_EE(csinh, 0, 0, 0, 0); + TEST_EE(csinh, 0, NZERO, 0, NZERO); + TEST_EE(csinh, NZERO, 0, NZERO, 0); + TEST_EE(csinh, NZERO, NZERO, NZERO, NZERO); + + /* sign of real part is unspecified */ + TEST_RAISES_UNSPECIFIED2(csinh, 0, INFINITY, 0, NAN, \ + NZERO, NAN, FE_INVALID); + + /* sign of real part is unspecified */ + TEST_UNSPECIFIED2(csinh, 0, NAN, 0, NAN, NZERO, NAN); + TEST_UNSPECIFIED2(csinh, NZERO, NAN, 0, NAN, NZERO, NAN); + + TEST_RAISES(csinh, 2.0, INFINITY, NAN, NAN, FE_INVALID); + TEST_RAISES(csinh, 2.0, -INFINITY, NAN, NAN, FE_INVALID); + TEST_RAISES(csinh, -2.0, INFINITY, NAN, NAN, FE_INVALID); + TEST_RAISES(csinh, -2.0, -INFINITY, NAN, NAN, FE_INVALID); + + /* can raise FE_INVALID or not */ + TEST_EE(csinh, 2.0, NAN, NAN, NAN); + TEST_EE(csinh, -2.0, NAN, NAN, NAN); + + TEST_EE(csinh, INFINITY, 0, INFINITY, 0); + TEST_EE(csinh, INFINITY, NZERO, INFINITY, NZERO); + TEST_EE(csinh, -INFINITY, 0, -INFINITY, 0); + TEST_EE(csinh, -INFINITY, NZERO, -INFINITY, NZERO); + + TEST_EE(csinh, INFINITY, 1.0, INFINITY, INFINITY); + TEST_EE(csinh, INFINITY, -1.0, INFINITY, -INFINITY); + TEST_EE(csinh, -INFINITY, 1.0, -INFINITY, INFINITY); + TEST_EE(csinh, -INFINITY, -1.0, -INFINITY, -INFINITY); + TEST_EE(csinh, INFINITY, 2.0, -INFINITY, INFINITY); + TEST_EE(csinh, INFINITY, -2.0, -INFINITY, -INFINITY); + TEST_EE(csinh, -INFINITY, 2.0, INFINITY, INFINITY); + TEST_EE(csinh, -INFINITY, -2.0, INFINITY, -INFINITY); + TEST_EE(csinh, INFINITY, 4.0, -INFINITY, -INFINITY); + TEST_EE(csinh, INFINITY, -4.0, -INFINITY, INFINITY); + TEST_EE(csinh, -INFINITY, 4.0, INFINITY, -INFINITY); + TEST_EE(csinh, -INFINITY, -4.0, INFINITY, INFINITY); + TEST_EE(csinh, INFINITY, 5.0, INFINITY, -INFINITY); + TEST_EE(csinh, INFINITY, -5.0, INFINITY, INFINITY); + TEST_EE(csinh, -INFINITY, 5.0, -INFINITY, -INFINITY); + TEST_EE(csinh, -INFINITY, -5.0, -INFINITY, INFINITY); + + /* sign of real part is unspecified */ + TEST_RAISES_UNSPECIFIED2(csinh, INFINITY, INFINITY, INFINITY, NAN, \ + -INFINITY, NAN, FE_INVALID); + + /* sign of real part is unspecified */ + TEST_UNSPECIFIED2(csinh, INFINITY, NAN, INFINITY, NAN, -INFINITY, NAN); + TEST_UNSPECIFIED2(csinh, -INFINITY, NAN, INFINITY, NAN, -INFINITY, NAN); + + TEST_EE(csinh, NAN, 0, NAN, 0); + TEST_EE(csinh, NAN, NZERO, NAN, NZERO); + + /* can raise FE_INVALID or not */ + TEST_EE(csinh, NAN, 2.0, NAN, NAN); + TEST_EE(csinh, NAN, -2.0, NAN, NAN); + + TEST_EE(csinh, NAN, NAN, NAN, NAN); + + TEST_CC(csinh, 0.5, 0, ADDSUFFIX(sinh)(0.5), 0); + + return ret; +} +#endif + +#ifdef CTANH +int test_ctanh() +{ + int ret = 1; + /* ctanh(conj(z)) = conj(ctanh(z)) and ctanh is odd */ + TEST_EE(ctanh, 0, 0, 0, 0); + TEST_EE(ctanh, 0, NZERO, 0, NZERO); + TEST_EE(ctanh, NZERO, 0, NZERO, 0); + TEST_EE(ctanh, NZERO, NZERO, NZERO, NZERO); + + TEST_RAISES(ctanh, 2.0, INFINITY, NAN, NAN, FE_INVALID); + TEST_RAISES(ctanh, -2.0, INFINITY, NAN, NAN, FE_INVALID); + + /* can raise FE_INVALID or not */ + TEST_EE(ctanh, 2.0, NAN, NAN, NAN); + TEST_EE(ctanh, -2.0, NAN, NAN, NAN); + + TEST_EE(ctanh, INFINITY, 1.0, 1.0, 0); + TEST_EE(ctanh, INFINITY, -1.0, 1.0, NZERO); + TEST_EE(ctanh, -INFINITY, 1.0, -1.0, 0); + TEST_EE(ctanh, -INFINITY, -1.0, -1.0, NZERO); + TEST_EE(ctanh, INFINITY, 2.0, 1.0, NZERO); + TEST_EE(ctanh, INFINITY, -2.0, 1.0, 0); + TEST_EE(ctanh, -INFINITY, 2.0, -1.0, NZERO); + TEST_EE(ctanh, -INFINITY, -2.0, -1.0, 0); + + /* sign of imaginary part is unspecified */ + TEST_UNSPECIFIED2(ctanh, INFINITY, INFINITY, 1, 0, 1, NZERO); + TEST_UNSPECIFIED2(ctanh, INFINITY, -INFINITY, 1, 0, 1, NZERO); + TEST_UNSPECIFIED2(ctanh, -INFINITY, INFINITY, -1, 0, -1, NZERO); + TEST_UNSPECIFIED2(ctanh, -INFINITY, -INFINITY, -1, 0, -1, NZERO); + + /* sign of imaginary part is unspecified */ + TEST_UNSPECIFIED2(ctanh, INFINITY, NAN, 1, 0, 1, NZERO); + TEST_UNSPECIFIED2(ctanh, -INFINITY, NAN, -1, 0, -1, NZERO); + + TEST_EE(ctanh, NAN, 0, NAN, 0); + TEST_EE(ctanh, NAN, NZERO, NAN, NZERO); + + /* can raise FE_INVALID or not */ + TEST_EE(ctanh, NAN, 2.0, NAN, NAN); + TEST_EE(ctanh, NAN, -2.0, NAN, NAN); + + TEST_EE(ctanh, NAN, NAN, NAN, NAN); + + TEST_CC(ctanh, 0.5, 0, ADDSUFFIX(tanh)(0.5), 0); + + TEST_CC(ctanh, 1000, 0, 1, 0); + TEST_CC(ctanh, -1000, 0, -1, 0); + + return ret; +} +#endif + +#ifdef CEXP +int test_cexp() +{ + int ret = 1; + /* cexp(conj(z)) = conj(cexp(z)) */ + TEST_EE(cexp, 0, 0, 1, 0); + TEST_EE(cexp, 0, NZERO, 1, NZERO); + + TEST_EE(cexp, NZERO, 0, 1, 0); + TEST_EE(cexp, NZERO, NZERO, 1, NZERO); + + TEST_RAISES(cexp, 2.0, INFINITY, NAN, NAN, FE_INVALID); + TEST_RAISES(cexp, 2.0, -INFINITY, NAN, NAN, FE_INVALID); + + /* can raise FE_INVALID or not */ + TEST_EE(cexp, 42.0, NAN, NAN, NAN); + + TEST_EE(cexp, INFINITY, 0, INFINITY, 0); + TEST_EE(cexp, INFINITY, NZERO, INFINITY, NZERO); + + TEST_EE(cexp, -INFINITY, 1.0, 0, 0); + TEST_EE(cexp, -INFINITY, -1.0, 0, NZERO); + TEST_EE(cexp, -INFINITY, 2.0, NZERO, 0); + TEST_EE(cexp, -INFINITY, -2.0, NZERO, NZERO); + TEST_EE(cexp, -INFINITY, 4.0, NZERO, NZERO); + TEST_EE(cexp, -INFINITY, -4.0, NZERO, 0); + TEST_EE(cexp, -INFINITY, 5.0, 0, NZERO); + TEST_EE(cexp, -INFINITY, -5.0, 0, 0); + + TEST_EE(cexp, INFINITY, 1.0, INFINITY, INFINITY); + TEST_EE(cexp, INFINITY, -1.0, INFINITY, -INFINITY); + TEST_EE(cexp, INFINITY, 2.0, -INFINITY, INFINITY); + TEST_EE(cexp, INFINITY, -2.0, -INFINITY, -INFINITY); + TEST_EE(cexp, INFINITY, 4.0, -INFINITY, -INFINITY); + TEST_EE(cexp, INFINITY, -4.0, -INFINITY, INFINITY); + TEST_EE(cexp, INFINITY, 5.0, INFINITY, -INFINITY); + TEST_EE(cexp, INFINITY, -5.0, INFINITY, INFINITY); + + /* signs of both parts are unspecified */ + TEST_UNSPECIFIED4(cexp, -INFINITY, INFINITY, 0, 0, NZERO, 0, \ + 0, NZERO, NZERO, NZERO); + TEST_UNSPECIFIED4(cexp, -INFINITY, -INFINITY, 0, 0, NZERO, 0, \ + 0, NZERO, NZERO, NZERO); + + /* sign of real part is unspecifed */ + TEST_RAISES_UNSPECIFIED2(cexp, INFINITY, INFINITY, INFINITY, \ + NAN, -INFINITY, NAN, FE_INVALID); + + /* signs of both parts are unspecified */ + TEST_UNSPECIFIED4(cexp, -INFINITY, NAN, 0, 0, NZERO, 0, \ + 0, NZERO, NZERO, NZERO); + + /* sign of real part is unspecified */ + TEST_UNSPECIFIED2(cexp, INFINITY, NAN, INFINITY, NAN, -INFINITY, NAN); + + TEST_EE(cexp, NAN, 0, NAN, 0); + TEST_EE(cexp, NAN, NZERO, NAN, NZERO); + + /* can raise FE_INVALID or not */ + TEST_EE(cexp, NAN, 2.0, NAN, NAN); + TEST_EE(cexp, NAN, -2.0, NAN, NAN); + + TEST_EE(cexp, NAN, NAN, NAN, NAN); + + TEST_CC(cexp, 0.5, 0, ADDSUFFIX(exp)(0.5), 0); + + TEST_CC(cexp, 1, 0, M_E, 0); + TEST_CC(cexp, 0, 1, ADDSUFFIX(cos)(1), ADDSUFFIX(sin)(1)); + TEST_CC(cexp, 1, 1, M_E*ADDSUFFIX(cos)(1), M_E*ADDSUFFIX(sin)(1)); + + return ret; +} +#endif + +#ifdef CLOG +int test_clog() +{ + int ret = 1; + /* clog(conj(z)) = conj(clog(z)) */ + TEST_RAISES(clog, NZERO, 0, -INFINITY, NPY_PI, FE_DIVBYZERO); + TEST_RAISES(clog, NZERO, NZERO, -INFINITY, -NPY_PI, FE_DIVBYZERO); + + TEST_RAISES(clog, 0, 0, -INFINITY, 0, FE_DIVBYZERO); + TEST_RAISES(clog, 0, NZERO, -INFINITY, NZERO, FE_DIVBYZERO); + + TEST_EC(clog, 2.0, INFINITY, INFINITY, NPY_PI_2); + TEST_EC(clog, 2.0, -INFINITY, INFINITY, -NPY_PI_2); + + /* can raise FE_INVALID or not */ + TEST_EE(clog, 2.0, NAN, NAN, NAN); + + TEST_EC(clog, -INFINITY, 2.0, INFINITY, NPY_PI); + TEST_EC(clog, -INFINITY, -2.0, INFINITY, -NPY_PI); + + TEST_EE(clog, INFINITY, 2.0, INFINITY, 0); + TEST_EE(clog, INFINITY, -2.0, INFINITY, NZERO); + + TEST_EC(clog, -INFINITY, INFINITY, INFINITY, 0.75 * NPY_PI); + TEST_EC(clog, -INFINITY, -INFINITY, INFINITY, -0.75 * NPY_PI); + + TEST_EC(clog, INFINITY, INFINITY, INFINITY, 0.25 * NPY_PI); + TEST_EC(clog, INFINITY, -INFINITY, INFINITY, -0.25 * NPY_PI); + + TEST_EE(clog, INFINITY, NAN, INFINITY, NAN); + TEST_EE(clog, -INFINITY, NAN, INFINITY, NAN); + + /* can raise FE_INVALID or not */ + TEST_EE(clog, NAN, 2.0, NAN, NAN); + TEST_EE(clog, NAN, -2.0, NAN, NAN); + + TEST_EE(clog, NAN, INFINITY, INFINITY, NAN); + TEST_EE(clog, NAN, -INFINITY, INFINITY, NAN); + + TEST_EE(clog, NAN, NAN, NAN, NAN); + + TEST_BRANCH_CUT(clog, -0.5, 0, 0, 1, 1, -1, 1); + + TEST_CC(clog, 0.5, 0, ADDSUFFIX(log)(0.5), 0); + + TEST_CC(clog, 1, 0, 0, 0); + TEST_CC(clog, 1, 2, 0.80471895621705014, 1.1071487177940904); + + return ret; +} +#endif + +#ifdef CPOW +int test_cpow() +{ + int ret = 1; + + /* there are _no_ annex G values for cpow. */ + /* We can check for branch cuts in here */ + + /* tests from test_umath.py: TestPower: test_power_complex */ + TEST_CPOW_CC(1, 2, 0, 0, 1, 0); + TEST_CPOW_CC(2, 3, 0, 0, 1, 0); + TEST_CPOW_CC(3, 4, 0, 0, 1, 0); + + TEST_CPOW_CC(1, 2, 1, 0, 1, 2); + TEST_CPOW_CC(2, 3, 1, 0, 2, 3); + TEST_CPOW_CC(3, 4, 1, 0, 3, 4); + + TEST_CPOW_CC(1, 2, 2, 0, -3, 4); + TEST_CPOW_CC(2, 3, 2, 0, -5, 12); + TEST_CPOW_CC(3, 4, 2, 0, -7, 24); + + TEST_CPOW_CC(1, 2, 3, 0, -11, -2); + TEST_CPOW_CC(2, 3, 3, 0, -46, 9); + TEST_CPOW_CC(3, 4, 3, 0, -117, 44); + + TEST_CPOW_CC(1, 2, 4, 0, -7, -24); + TEST_CPOW_CC(2, 3, 4, 0, -119, -120); + TEST_CPOW_CC(3, 4, 4, 0, -527, -336); + + TEST_CPOW_CC(1, 2, -1, 0, 1.0/5.0, -2.0/5.0); + TEST_CPOW_CC(2, 3, -1, 0, 2.0/13.0, -3.0/13.0); + TEST_CPOW_CC(3, 4, -1, 0, 3.0/25.0, -4.0/25.0); + + TEST_CPOW_CC(1, 2, -2, 0, -3.0/25.0, -4.0/25.0); + TEST_CPOW_CC(2, 3, -2, 0, -5.0/169.0, -12.0/169.0); + TEST_CPOW_CC(3, 4, -2, 0, -7.0/625.0, -24.0/625.0); + + TEST_CPOW_CC(1, 2, -3, 0, -11.0/125.0, 2.0/125.0); + TEST_CPOW_CC(2, 3, -3, 0, -46.0/2197.0, -9.0/2197.0); + TEST_CPOW_CC(3, 4, -3, 0, -117.0/15625.0, -44.0/15625.0); + + TEST_CPOW_CC(1, 2, 0.5, 0, 1.272019649514069, 0.7861513777574233); + TEST_CPOW_CC(2, 3, 0.5, 0, 1.6741492280355401, 0.895977476129838); + TEST_CPOW_CC(3, 4, 0.5, 0, 2, 1); + + TEST_CPOW_CC(1, 2, 14, 0, -76443, 16124); + TEST_CPOW_CC(2, 3, 14, 0, 23161315, 58317492); + TEST_CPOW_CC(3, 4, 14, 0, 5583548873, 2465133864); + + TEST_CPOW_EE(0, INFINITY, 1, 0, 0, INFINITY); + TEST_CPOW_EE(0, INFINITY, 2, 0, -INFINITY, NAN); + TEST_CPOW_EE(0, INFINITY, 3, 0, NAN, NAN); + + TEST_CPOW_EE(1, INFINITY, 1, 0, 1, INFINITY); + TEST_CPOW_EE(1, INFINITY, 2, 0, -INFINITY, INFINITY); + TEST_CPOW_EE(1, INFINITY, 3, 0, -INFINITY, NAN); + + /* tests from test_umath.py: TestPower: test_power_zero */ + TEST_CPOW_CC(0, 0, 0.33, 0, 0, 0); + TEST_CPOW_CC(0, 0, 0.5, 0, 0, 0); + TEST_CPOW_CC(0, 0, 1.0, 0, 0, 0); + TEST_CPOW_CC(0, 0, 1.5, 0, 0, 0); + TEST_CPOW_CC(0, 0, 2.0, 0, 0, 0); + TEST_CPOW_CC(0, 0, 3.0, 0, 0, 0); + TEST_CPOW_CC(0, 0, 4.0, 0, 0, 0); + TEST_CPOW_CC(0, 0, 5.0, 0, 0, 0); + TEST_CPOW_CC(0, 0, 6.6, 0, 0, 0); + + TEST_CPOW_EE(0, 0, 0, 0, 1, 0); + TEST_CPOW_EE(0, 0, 0, 1, NAN, NAN); + + TEST_CPOW_EE(0, 0, -0.33, 0, NAN, NAN); + TEST_CPOW_EE(0, 0, -0.5, 0, NAN, NAN); + TEST_CPOW_EE(0, 0, -1.0, 0, NAN, NAN); + TEST_CPOW_EE(0, 0, -1.5, 0, NAN, NAN); + TEST_CPOW_EE(0, 0, -2.0, 0, NAN, NAN); + TEST_CPOW_EE(0, 0, -3.0, 0, NAN, NAN); + TEST_CPOW_EE(0, 0, -4.0, 0, NAN, NAN); + TEST_CPOW_EE(0, 0, -5.0, 0, NAN, NAN); + TEST_CPOW_EE(0, 0, -6.6, 0, NAN, NAN); + TEST_CPOW_EE(0, 0, -1, 0.2, NAN, NAN); + + /* tests from test_umath_complex.py: TestCpow: test_simple + * --- skip, duplicating existing tests --- + */ + + /* tests from test_umath_complex.py: TestCpow: test_scalar, test_array + * these tests are equilvent for this level. + */ + TEST_CPOW_CC(1, 0, 1, 0, 1, 0); + TEST_CPOW_CC(1, 0, 0, 1, 1, 0); + TEST_CPOW_CC(1, 0, -0.5, 1.5, 1, 0); + TEST_CPOW_CC(1, 0, 2, 0, 1, 0); + TEST_CPOW_CC(1, 0, 3, 0, 1, 0); + + TEST_CPOW_CC(0, 1, 1, 0, 0, 1); + TEST_CPOW_CC(0, 1, 0, 1, ADDSUFFIX(exp)(-NPY_PI_2), 0); + TEST_CPOW_CC(0, 1, -0.5, 1.5, 0.067019739708273365, 0.067019739708273365); + TEST_CPOW_CC(0, 1, 2, 0, -1, 0); + TEST_CPOW_CC(0, 1, 3, 0, 0, -1); + + TEST_CPOW_CC(2, 0, 1, 0, 2, 0); + TEST_CPOW_CC(2, 0, 0, 1, ADDSUFFIX(cos)(NPY_LOG2E), ADDSUFFIX(sin)(NPY_LOG2E)); + TEST_CPOW_CC(2, 0, 2, 0, 4, 0); + TEST_CPOW_CC(2, 0, 3, 0, 8, 0); + + TEST_CPOW_CC(2.5, 0.375, 1, 0, 2.5, 0.375); + TEST_CPOW_CC(2.5, 0.375, 0, 1, 0.51691507509598866, 0.68939360813851125); + TEST_CPOW_CC(2.5, 0.375, -0.5, 1.5, 0.12646517347496394, 0.48690593271654437); + TEST_CPOW_CC(2.5, 0.375, 2, 0, 391.0/64.0, 15.0/8.0); + TEST_CPOW_CC(2.5, 0.375, 3, 0, 1865.0/128.0, 3573.0/512.0); + + TEST_CPOW_EE(INFINITY, 0, 1, 0, NAN, NAN); + TEST_CPOW_EE(INFINITY, 0, 0, 1, NAN, NAN); + TEST_CPOW_EE(INFINITY, 0, -0.5, 1.5, NAN, NAN); + TEST_CPOW_EE(INFINITY, 0, 2, 0, NAN, NAN); + TEST_CPOW_EE(INFINITY, 0, 3, 0, NAN, NAN); + + TEST_CPOW_EE(NAN, 0, 1, 0, NAN, NAN); + TEST_CPOW_EE(NAN, 0, 0, 1, NAN, NAN); + TEST_CPOW_EE(NAN, 0, -0.5, 1.5, NAN, NAN); + TEST_CPOW_EE(NAN, 0, 2, 0, NAN, NAN); + TEST_CPOW_EE(NAN, 0, 3, 0, NAN, NAN); + + return ret; +} +#endif + +#ifdef CSQRT +int test_csqrt() +{ + int ret = 1; + /* csqrt(conj(z)) = conj(csqrt(z)) */ + TEST_EE(csqrt, 0, 0, 0, 0); + TEST_EE(csqrt, 0, NZERO, 0, NZERO); + + TEST_EE(csqrt, NZERO, 0, 0, 0); + TEST_EE(csqrt, NZERO, NZERO, 0, NZERO); + + TEST_EE(csqrt, 2.0, INFINITY, INFINITY, INFINITY); + TEST_EE(csqrt, 2.0, -INFINITY, INFINITY, -INFINITY); + + TEST_EE(csqrt, NAN, INFINITY, INFINITY, INFINITY); + TEST_EE(csqrt, NAN, -INFINITY, INFINITY, -INFINITY); + + TEST_EE(csqrt, INFINITY, INFINITY, INFINITY, INFINITY); + TEST_EE(csqrt, INFINITY, -INFINITY, INFINITY, -INFINITY); + + TEST_EE(csqrt, -INFINITY, INFINITY, INFINITY, INFINITY); + TEST_EE(csqrt, -INFINITY, -INFINITY, INFINITY, -INFINITY); + + /* can raise FE_INVALID or not */ + TEST_EE(csqrt, 2.0, NAN, NAN, NAN); + + TEST_EE(csqrt, -INFINITY, 2.0, 0, INFINITY); + TEST_EE(csqrt, -INFINITY, -2.0, 0, -INFINITY); + + TEST_EE(csqrt, INFINITY, 2.0, INFINITY, 0); + TEST_EE(csqrt, INFINITY, -2.0, INFINITY, NZERO); + + /* sign of imaginary part is unspecified */ + TEST_UNSPECIFIED2(csqrt, -INFINITY, NAN, NAN, INFINITY, NAN, -INFINITY); + + TEST_EE(csqrt, INFINITY, NAN, INFINITY, NAN); + + /* can raise FE_INVALID or not */ + TEST_EE(csqrt, NAN, 2.0, NAN, NAN); + TEST_EE(csqrt, NAN, -2.0, NAN, NAN); + + TEST_EE(csqrt, NAN, NAN, NAN, NAN); + + TEST_BRANCH_CUT(csqrt, -0.5, 0, 0, 1, 1, -1, 1); + + TEST_CC(csqrt, 0.5, 0, ADDSUFFIX(sqrt)(0.5), 0); + + TEST_CC(csqrt, 1, 0, 1, 0); + TEST_CC(csqrt, 0, 1, NPY_SQRT2/2.0, NPY_SQRT2/2.0); + TEST_CC(csqrt, -1, 0, 0, 1); + TEST_CC(csqrt, 1, 1, 1.0986841134678100, 0.4550898605622273); + TEST_CC(csqrt, 1, -1, 1.0986841134678100, -0.4550898605622273); + + return ret; +} +#endif + +int main(int argc, char** argv) +{ +#ifdef CACOS + return !test_cacos(); +#endif +#ifdef CASIN + return !test_casin(); +#endif +#ifdef CATAN + return !test_catan(); +#endif +#ifdef CACOSH + return !test_cacosh(); +#endif +#ifdef CASINH + return !test_casinh(); +#endif +#ifdef CATANH + return !test_catanh(); +#endif +#ifdef CCOS + return !test_ccos(); +#endif +#ifdef CSIN + return !test_csin(); +#endif +#ifdef CTAN + return !test_ctan(); +#endif +#ifdef CCOSH + return !test_ccosh(); +#endif +#ifdef CSINH + return !test_csinh(); +#endif +#ifdef CTANH + return !test_ctanh(); +#endif +#ifdef CEXP + return !test_cexp(); +#endif +#ifdef CLOG + return !test_clog(); +#endif +#ifdef CPOW + return !test_cpow(); +#endif +#ifdef CSQRT + return !test_csqrt(); +#endif +} + From d504ea18ed73bd8d8f671a798d1851f545f61a9f Mon Sep 17 00:00:00 2001 From: Eric Moore Date: Wed, 2 Oct 2013 17:35:27 -0400 Subject: [PATCH 09/35] BUG: NPY_LOGE2 is log(2) --- numpy/core/test_c99complex.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/numpy/core/test_c99complex.c b/numpy/core/test_c99complex.c index c8add9745ac5..9251a0123256 100644 --- a/numpy/core/test_c99complex.c +++ b/numpy/core/test_c99complex.c @@ -16,7 +16,7 @@ #define FMT "%.8e" #define NPY_PI_2 1.570796326794896619231321691639751442f #define NPY_PI 3.141592653589793238462643383279502884f -#define NPY_LOG2E 1.442695040888963407359924681001892137f +#define NPY_LOGE2 0.693147180559945309417232121458176568f #define NPY_SQRT2 1.414213562373095048801688724209698079f #else #ifdef DOUBLE @@ -28,7 +28,7 @@ #define FMT "%.16e" #define NPY_PI_2 1.570796326794896619231321691639751442 #define NPY_PI 3.141592653589793238462643383279502884 -#define NPY_LOG2E 1.442695040888963407359924681001892137 +#define NPY_LOGE2 0.693147180559945309417232121458176568 #define NPY_SQRT2 1.414213562373095048801688724209698079 #else #ifdef LONGDOUBLE @@ -40,7 +40,7 @@ #define FMT "%.18Le" #define NPY_PI_2 1.570796326794896619231321691639751442L #define NPY_PI 3.141592653589793238462643383279502884L -#define NPY_LOG2E 1.442695040888963407359924681001892137L +#define NPY_LOGE2 0.693147180559945309417232121458176568L #define NPY_SQRT2 1.414213562373095048801688724209698079L #else #error "Define FLOAT or DOUBLE or LONGDOUBLE" @@ -1452,7 +1452,7 @@ int test_cpow() TEST_CPOW_CC(0, 1, 3, 0, 0, -1); TEST_CPOW_CC(2, 0, 1, 0, 2, 0); - TEST_CPOW_CC(2, 0, 0, 1, ADDSUFFIX(cos)(NPY_LOG2E), ADDSUFFIX(sin)(NPY_LOG2E)); + TEST_CPOW_CC(2, 0, 0, 1, ADDSUFFIX(cos)(NPY_LOGE2), ADDSUFFIX(sin)(NPY_LOGE2)); TEST_CPOW_CC(2, 0, 2, 0, 4, 0); TEST_CPOW_CC(2, 0, 3, 0, 8, 0); From 5362b35db178902cce86b46f758ae13a2136fdc7 Mon Sep 17 00:00:00 2001 From: Eric Moore Date: Wed, 2 Oct 2013 18:05:49 -0400 Subject: [PATCH 10/35] BUG: Test aginst +/-TANH_HUGE This is a bug introduced when adapting the FreeBSD implementation. Since they play some games extracting parts of the double and comparing them as unsigned ints and we don't. We don't since that doesn't generalize to float, and long double as we do in npy_math_complex.c.src --- numpy/core/src/npymath/npy_math_complex.c.src | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/numpy/core/src/npymath/npy_math_complex.c.src b/numpy/core/src/npymath/npy_math_complex.c.src index 113ee6405cac..fcf043df6bac 100644 --- a/numpy/core/src/npymath/npy_math_complex.c.src +++ b/numpy/core/src/npymath/npy_math_complex.c.src @@ -556,7 +556,7 @@ static NPY_INLINE @ctype@ cmuli@c@(@ctype@ a) * approximation sinh^2(huge) ~= exp(2*huge) / 4. * We use a modified formula to avoid spurious overflow. */ - if (x >= TANH@C@_HUGE) { + if (npy_fabs@c@(x) >= TANH@C@_HUGE) { @type@ exp_mx = npy_exp@c@(-npy_fabs@c@(x)); return (npy_cpack@c@(npy_copysign@c@(1, x), 4 * npy_sin@c@(y) * npy_cos@c@(y) * exp_mx * exp_mx)); From e61e74f2bd1709c1493fae27c2b6485ec2fa1e50 Mon Sep 17 00:00:00 2001 From: Eric Moore Date: Wed, 2 Oct 2013 18:09:44 -0400 Subject: [PATCH 11/35] TST: check for tanh(1000+0j) == 1 + 0j etc. This is a test for the fix for gh-2321. --- numpy/core/tests/test_umath_complex.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/numpy/core/tests/test_umath_complex.py b/numpy/core/tests/test_umath_complex.py index 4f3da4397acf..1b80fbc1212a 100644 --- a/numpy/core/tests/test_umath_complex.py +++ b/numpy/core/tests/test_umath_complex.py @@ -517,6 +517,16 @@ def test_special_values(self): yield check_real_value, ncu._arg, np.nan, np.inf, np.nan, False yield check_real_value, ncu._arg, np.inf, np.nan, np.nan, False +class TestCtan(object): + def test_simple(self): + check_complex_value(ncu.tan, 0, 1000, 0, 1, True) + check_complex_value(ncu.tan, 0, -1000, 0, -1, True) + +class TestCtanh(object): + def test_simple(self): + check_complex_value(ncu.tanh, 1000, 0, 1, 0, True) + check_complex_value(ncu.tanh, -1000, 0, -1, 0, True) + def check_real_value(f, x1, y1, x, exact=True): z1 = np.array([complex(x1, y1)]) if exact: From 852cd10423a081d77262b04775614e8bebcff5cf Mon Sep 17 00:00:00 2001 From: Eric Moore Date: Wed, 2 Oct 2013 18:50:01 -0400 Subject: [PATCH 12/35] BUG: Fix up some corner cases in npy_cexp The failing c99 annex G values for cexp were: cexp(Inf, -0) == (NAN, -0) cexp(Inf, Inf) == (+-Inf, NAN) + raises FE_INVALID The first returned (NAN, 0) and the second was correct but failed to raise the invalid floating-point exception. --- numpy/core/src/npymath/npy_math_complex.c.src | 3 ++- numpy/core/tests/test_umath_complex.py | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/numpy/core/src/npymath/npy_math_complex.c.src b/numpy/core/src/npymath/npy_math_complex.c.src index fcf043df6bac..7bd06cc8a53e 100644 --- a/numpy/core/src/npymath/npy_math_complex.c.src +++ b/numpy/core/src/npymath/npy_math_complex.c.src @@ -172,7 +172,7 @@ static NPY_INLINE @ctype@ cmuli@c@(@ctype@ a) } else if (npy_isnan(r)) { /* r is nan */ if (i == 0) { - ret = npy_cpack@c@(r, 0); + ret = z; } else { ret = npy_cpack@c@(r, npy_copysign@c@(NPY_NAN, i)); } @@ -188,6 +188,7 @@ static NPY_INLINE @ctype@ cmuli@c@(@ctype@ a) ret = npy_cpack@c@(r * c, r * s); } else { /* x = +inf, y = +-inf | nan */ + npy_set_floatstatus_invalid(); ret = npy_cpack@c@(r, NPY_NAN); } } else { diff --git a/numpy/core/tests/test_umath_complex.py b/numpy/core/tests/test_umath_complex.py index 1b80fbc1212a..620134f12510 100644 --- a/numpy/core/tests/test_umath_complex.py +++ b/numpy/core/tests/test_umath_complex.py @@ -123,6 +123,11 @@ def _check_inf_nan(dummy): # cexp(nan + nani) is nan + nani yield check, f, np.nan, np.nan, np.nan, np.nan + # cexp(nan +-0) is nan +-0 + yield check, f, np.nan, np.PZERO, np.nan, np.PZERO, True + yield check, f, np.nan, np.NZERO, np.nan, np.NZERO, True + + @dec.knownfailureif(True, "cexp(nan + 0I) is wrong on most implementations") def test_special_values2(self): # XXX: most implementations get it wrong here (including glibc <= 2.10) From 0f57b424482c9001e53daba233df67622b947cc9 Mon Sep 17 00:00:00 2001 From: Eric Moore Date: Wed, 2 Oct 2013 19:00:33 -0400 Subject: [PATCH 13/35] MAINT: remove debug print statement --- numpy/core/setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/numpy/core/setup.py b/numpy/core/setup.py index add5af755597..4f462205ea18 100644 --- a/numpy/core/setup.py +++ b/numpy/core/setup.py @@ -238,7 +238,6 @@ def check_prec(prec): fp.close() precname = {'f':'FLOAT', '':'DOUBLE', 'l':'LONGDOUBLE'}[prec] for f in exists: - print("ERIC1") body = obody.replace('PYTESTPRECISION', precname).replace('PYTESTFUNC', f.upper()) if config.try_run(body, libraries=mathlibs): priv.append((fname2def(f + prec), 1)) From eb065574da53ed7e81b8424ab1ee0d6b04b68feb Mon Sep 17 00:00:00 2001 From: Eric Moore Date: Thu, 3 Oct 2013 20:22:57 -0400 Subject: [PATCH 14/35] MAINT: fix two typos in test_c99complex.c --- numpy/core/test_c99complex.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/numpy/core/test_c99complex.c b/numpy/core/test_c99complex.c index 9251a0123256..09f11affb023 100644 --- a/numpy/core/test_c99complex.c +++ b/numpy/core/test_c99complex.c @@ -11,7 +11,7 @@ #define TYPE float #define SUFFIX f #define EPS FLT_EPSILON -#define CLOSE_ATOL 3 +#define CLOSE_ATOL 0 #define CLOSE_RTOL 1e-5 #define FMT "%.8e" #define NPY_PI_2 1.570796326794896619231321691639751442f @@ -126,7 +126,7 @@ const TYPE NZERO = -1.0 * 0.0; TYPE der4 = er4; \ TYPE dei4 = ei4; \ TYPE complex x = cpack(dxr, dxi); \ - TYPE complex r = func(x); \ + TYPE complex r = ADDSUFFIX(func)(x); \ TYPE rr = ADDSUFFIX(creal)(r); \ TYPE ri = ADDSUFFIX(cimag)(r); \ if (!((isequal(rr, der1) && isequal(ri, dei1)) || \ @@ -307,7 +307,7 @@ typedef TYPE (*realfunc)(TYPE); int check_branch_cut(complexfunc cfunc, TYPE complex x0, TYPE complex dx, int re_sign, int im_sign, int sig_zero_ok) { - const TYPE scale = EPS * 1e3; + const TYPE scale = EPS * 1e2; const TYPE atol = 1e-4; TYPE complex shift = dx*scale*ADDSUFFIX(cabs)(x0)/ADDSUFFIX(cabs)(dx); From 77705ab0cabad32c7a44e76a36d4a80ecb1448fc Mon Sep 17 00:00:00 2001 From: Eric Moore Date: Thu, 3 Oct 2013 20:25:26 -0400 Subject: [PATCH 15/35] ENH: Be more careful with large real parts in npy_cexp This change is adapted from k_exp.c in FreeBSD. The idea is to calculate a scaled exponential, which is then multiplied by cos(imag) and sin(imag) and rescaled to the appropriate value. This means that for float64, things like np.exp(complex(729, 1e-16)) which used to give inf+infj now gives (inf+3.987285262042597e+300j) --- numpy/core/src/npymath/npy_math_complex.c.src | 63 ++++++++++++++++--- 1 file changed, 56 insertions(+), 7 deletions(-) diff --git a/numpy/core/src/npymath/npy_math_complex.c.src b/numpy/core/src/npymath/npy_math_complex.c.src index 7bd06cc8a53e..79a04cdc9f49 100644 --- a/numpy/core/src/npymath/npy_math_complex.c.src +++ b/numpy/core/src/npymath/npy_math_complex.c.src @@ -147,6 +147,50 @@ static NPY_INLINE @ctype@ cmuli@c@(@ctype@ a) } #endif +/* cexp and (ccos, csin)h functions need to calculate exp scaled by another + * number. This can be difficult if exp(x) overflows. By doing this way, we + * don't risk overflowing exp. This likely raises floating-point exceptions, + * if we decide that we care. + * + * This is only useful over a limited range, (see below) an expects that the + * input values are in this range. + * + * This is based on the technique used in FreeBSD's __frexp_exp and + * __ldexp_(c)exp functions by David Schultz. + */ + +#define SCALED_CEXP_LOWERF 88.722839f +#define SCALED_CEXP_UPPERF 192.69492 +#define SCALED_CEXP_LOWER 710.47586007394386 +#define SCALED_CEXP_UPPER 1454.9159319953251 +#define SCALED_CEXP_LOWERL 11357.216553474703895L +#define SCALED_CEXP_UPPERL 22756.021937783004509L + +static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, int expt) +{ +#if @precision@ == 1 + const npy_int k = 235; +#endif +#if @precision@ == 2 + const npy_int k = 1799; +#endif +#if @precision@ == 3 + const npy_int k = 19547; +#endif + const @type@ kln2 = k * NPY_LOGE2; + @type@ mant, mantcos, mantsin; + npy_int ex, excos, exsin; + + mant = npy_frexp@c@(npy_exp@c@(x - kln2), &ex); + mantcos = npy_frexp@c@(npy_cos@c@(y), &excos); + mantsin = npy_frexp@c@(npy_sin@c@(y), &exsin); + + expt += ex + k; + return npy_cpack@c@( npy_ldexp@c@(mant * mantcos, expt + excos), + npy_ldexp@c@(mant * mantsin, expt + exsin)); +} + + #ifndef HAVE_CEXP@C@ @ctype@ npy_cexp@c@(@ctype@ z) { @@ -158,15 +202,20 @@ static NPY_INLINE @ctype@ cmuli@c@(@ctype@ a) i = npy_cimag@c@(z); if (npy_isfinite(r)) { - x = npy_exp@c@(r); + if (r >= SCALED_CEXP_LOWER@C@ && r <= SCALED_CEXP_UPPER@C@) { + ret = _npy_scaled_cexp@c@(r, i, 0); + } + else { + x = npy_exp@c@(r); - c = npy_cos@c@(i); - s = npy_sin@c@(i); + c = npy_cos@c@(i); + s = npy_sin@c@(i); - if (npy_isfinite(i)) { - ret = npy_cpack@c@(x * c, x * s); - } else { - ret = npy_cpack@c@(NPY_NAN, npy_copysign@c@(NPY_NAN, i)); + if (npy_isfinite(i)) { + ret = npy_cpack@c@(x * c, x * s); + } else { + ret = npy_cpack@c@(NPY_NAN, npy_copysign@c@(NPY_NAN, i)); + } } } else if (npy_isnan(r)) { From e574e299a781394a83cbcde0f9282fd0fecacafc Mon Sep 17 00:00:00 2001 From: Eric Moore Date: Thu, 3 Oct 2013 21:29:00 -0400 Subject: [PATCH 16/35] 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. --- numpy/core/src/npymath/npy_math_complex.c.src | 142 ++++++++++++++++-- 1 file changed, 131 insertions(+), 11 deletions(-) diff --git a/numpy/core/src/npymath/npy_math_complex.c.src b/numpy/core/src/npymath/npy_math_complex.c.src index 79a04cdc9f49..538c0f426719 100644 --- a/numpy/core/src/npymath/npy_math_complex.c.src +++ b/numpy/core/src/npymath/npy_math_complex.c.src @@ -157,16 +157,20 @@ static NPY_INLINE @ctype@ cmuli@c@(@ctype@ a) * * This is based on the technique used in FreeBSD's __frexp_exp and * __ldexp_(c)exp functions by David Schultz. + * + * SCALED_CEXP_LOWER = log(FLT_MAX) + * SCALED_CEXP_UPPER = log(2) + log(FLT_MAX) - log(FLT_TRUE_MIN), + * where FLT_TRUE_MIN is the smallest possible subnormal number. */ #define SCALED_CEXP_LOWERF 88.722839f -#define SCALED_CEXP_UPPERF 192.69492 -#define SCALED_CEXP_LOWER 710.47586007394386 +#define SCALED_CEXP_UPPERF 192.69492f +#define SCALED_CEXP_LOWER 710.47586007394386 #define SCALED_CEXP_UPPER 1454.9159319953251 #define SCALED_CEXP_LOWERL 11357.216553474703895L #define SCALED_CEXP_UPPERL 22756.021937783004509L -static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, int expt) +static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) { #if @precision@ == 1 const npy_int k = 235; @@ -291,7 +295,7 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, int expt) @type@ ax = npy_fabs@c@(npy_creal@c@(z)); @type@ ay = npy_fabs@c@(npy_cimag@c@(z)); @type@ rr, ri; - + if (ax > @TMAX@/4 || ay > @TMAX@/4) { rr = npy_log@c@(npy_hypot@c@(ax/2, ay/2)) + NPY_LOGE2@c@; } @@ -470,10 +474,8 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, int expt) #ifndef HAVE_CCOS@C@ @ctype@ npy_ccos@c@(@ctype@ z) { - @type@ x, y; - x = npy_creal@c@(z); - y = npy_cimag@c@(z); - return npy_cpack@c@(npy_cos@c@(x) * npy_cosh@c@(y), -(npy_sin@c@(x) * npy_sinh@c@(y))); + /* ccos(z) = ccosh(I * z) */ + return npy_ccosh@c@(npy_cpack@c@(-npy_cimag@c@(z), npy_creal@c@(z))); } #endif @@ -493,17 +495,135 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, int expt) /* ctan(z) = -I * ctanh(I * z) */ z = npy_ctanh@c@(npy_cpack@c@(-npy_cimag@c@(z), npy_creal@c@(z))); return (npy_cpack@c@(npy_cimag@c@(z), -npy_creal@c@(z))); -} +} #endif #ifndef HAVE_CCOSH@C@ +/* + * Taken from the msun library in FreeBSD, rev 226599. + * + * Hyperbolic cosine of a complex argument z = x + i y. + * + * cosh(z) = cosh(x+iy) + * = cosh(x) cos(y) + i sinh(x) sin(y). + * + * Exceptional values are noted in the comments within the source code. + * These values and the return value were taken from n1124.pdf. + * + * CCOSH_BIG is chosen such that + * spacing(0.5 * exp(CCOSH_BIG)) > 0.5*exp(-CCOSH_BIG) + * although the exact value assigned to CCOSH_BIG is not so important + */ +#if @precision@ == 1 +#define CCOSH_BIG 9.0f +#define CCOSH_HUGE 1.70141183e+38f +#endif +#if @precision@ == 2 +#define CCOSH_BIG 22.0 +#define CCOSH_HUGE 8.9884656743115795e+307 +#endif +#if @precision@ >= 3 +#define CCOSH_BIG 24.0L +#define CCOSH_HUGE 5.94865747678615882543e+4931L +#endif + @ctype@ npy_ccosh@c@(@ctype@ z) { - @type@ x, y; + @type@ x, y, h, absx; + npy_int xfinite, yfinite; + x = npy_creal@c@(z); y = npy_cimag@c@(z); - return npy_cpack@c@(npy_cos@c@(y)*npy_cosh@c@(x), npy_sin@c@(y)*npy_sinh@c@(x)); + + xfinite = npy_isfinite(x); + yfinite = npy_isfinite(y); + + /* Handle the nearly-non-exceptional cases where x and y are finite. */ + if (xfinite && yfinite) { + if (y == 0) + return npy_cpack@c@(npy_cosh@c@(x), x * y); + absx = npy_fabs@c@(x); + if (absx < CCOSH_BIG) /* small x: normal case */ + return npy_cpack@c@(npy_cosh@c@(x) * npy_cos@c@(y), npy_sinh@c@(x) * npy_sin@c@(y)); + + /* |x| >= 22, so cosh(x) ~= exp(|x|) */ + if (absx < SCALED_CEXP_LOWER@C@) { + /* x < 710: exp(|x|) won't overflow */ + h = npy_exp@c@(absx) * 0.5; + return npy_cpack@c@(h * npy_cos@c@(y), npy_copysign@c@(h, x) * npy_sin@c@(y)); + } else if (absx < SCALED_CEXP_UPPER@C@) { + /* x < 1455: scale to avoid overflow */ + z = _npy_scaled_cexp@c@(absx, y, -1); + return npy_cpack@c@(npy_creal@c@(z), npy_cimag@c@(z) * npy_copysign@c@(1, x)); + } else { + /* x >= 1455: the result always overflows */ + h = CCOSH_HUGE * x; + return npy_cpack@c@(h * h * npy_cos@c@(y), h * npy_sin@c@(y)); + } + } + + /* + * cosh(+-0 +- I Inf) = dNaN + I sign(d(+-0, dNaN))0. + * The sign of 0 in the result is unspecified. Choice = normally + * the same as dNaN. Raise the invalid floating-point exception. + * + * cosh(+-0 +- I NaN) = d(NaN) + I sign(d(+-0, NaN))0. + * The sign of 0 in the result is unspecified. Choice = normally + * the same as d(NaN). + */ + if (x == 0 && !yfinite) + return npy_cpack@c@(y - y, npy_copysign@c@(0, x * (y - y))); + + /* + * cosh(+-Inf +- I 0) = +Inf + I (+-)(+-)0. + * + * cosh(NaN +- I 0) = d(NaN) + I sign(d(NaN, +-0))0. + * The sign of 0 in the result is unspecified. + */ + if (y == 0 && !xfinite) + return npy_cpack@c@(x * x, npy_copysign@c@(0, x) * y); + + /* + * cosh(x +- I Inf) = dNaN + I dNaN. + * Raise the invalid floating-point exception for finite nonzero x. + * + * cosh(x + I NaN) = d(NaN) + I d(NaN). + * Optionally raises the invalid floating-point exception for finite + * nonzero x. Choice = don't raise (except for signaling NaNs). + */ + if (xfinite && !yfinite) + return npy_cpack@c@(y - y, x * (y - y)); + + /* + * cosh(+-Inf + I NaN) = +Inf + I d(NaN). + * + * cosh(+-Inf +- I Inf) = +Inf + I dNaN. + * The sign of Inf in the result is unspecified. Choice = always +. + * Raise the invalid floating-point exception. + * + * cosh(+-Inf + I y) = +Inf cos(y) +- I Inf sin(y) + */ + if (npy_isinf(x)) { + if (!yfinite) + return npy_cpack@c@(x * x, x * (y - y)); + return npy_cpack@c@((x * x) * npy_cos@c@(y), x * npy_sin@c@(y)); + } + + /* + * cosh(NaN + I NaN) = d(NaN) + I d(NaN). + * + * cosh(NaN +- I Inf) = d(NaN) + I d(NaN). + * Optionally raises the invalid floating-point exception. + * Choice = raise. + * + * cosh(NaN + I y) = d(NaN) + I d(NaN). + * Optionally raises the invalid floating-point exception for finite + * nonzero y. Choice = don't raise (except for signaling NaNs). + */ + return npy_cpack@c@((x * x) * (y - y), (x + x) * (y - y)); } +#undef CCOSH_BIG +#undef CCOSH_HUGE #endif #ifndef HAVE_CSINH@C@ From 37104817b7a0b8538934a4eea2071ef44c81bfaf Mon Sep 17 00:00:00 2001 From: Eric Moore Date: Thu, 3 Oct 2013 22:56:23 -0400 Subject: [PATCH 17/35] ENH: Import the csinh/csin implementation from FreeBSD The code from FreeBSD was lightly adapted to fit with the numpy style. With this commit, npy_csinh(f) and npy_csin(f) pass all of the tests in test_c99complex.c. --- numpy/core/src/npymath/npy_math_complex.c.src | 130 +++++++++++++++++- 1 file changed, 124 insertions(+), 6 deletions(-) diff --git a/numpy/core/src/npymath/npy_math_complex.c.src b/numpy/core/src/npymath/npy_math_complex.c.src index 538c0f426719..1e77184f20d0 100644 --- a/numpy/core/src/npymath/npy_math_complex.c.src +++ b/numpy/core/src/npymath/npy_math_complex.c.src @@ -482,10 +482,9 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) #ifndef HAVE_CSIN@C@ @ctype@ npy_csin@c@(@ctype@ z) { - @type@ x, y; - x = npy_creal@c@(z); - y = npy_cimag@c@(z); - return npy_cpack@c@(npy_sin@c@(x) * npy_cosh@c@(y), npy_cos@c@(x) * npy_sinh@c@(y)); + /* csin(z) = -I * csinh(I * z) */ + z = npy_csinh@c@(npy_cpack@c@(-npy_cimag@c@(z), npy_creal@c@(z))); + return npy_cpack@c@(npy_cimag@c@(z), -npy_creal@c@(z)); } #endif @@ -627,13 +626,132 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) #endif #ifndef HAVE_CSINH@C@ +/* + * Taken from the msun library in FreeBSD, rev 226599. + * + * Hyperbolic sine of a complex argument z = x + i y. + * + * sinh(z) = sinh(x+iy) + * = sinh(x) cos(y) + i cosh(x) sin(y). + * + * Exceptional values are noted in the comments within the source code. + * These values and the return value were taken from n1124.pdf. + */ + +#if @precision@ == 1 +#define CSINH_BIG 9.0f +#define CSINH_HUGE 1.70141183e+38f +#endif +#if @precision@ == 2 +#define CSINH_BIG 22.0 +#define CSINH_HUGE 8.9884656743115795e+307 +#endif +#if @precision@ >= 3 +#define CSINH_BIG 24.0L +#define CSINH_HUGE 5.94865747678615882543e+4931L +#endif + @ctype@ npy_csinh@c@(@ctype@ z) { - @type@ x, y; + @type@ x, y, h, absx; + npy_int xfinite, yfinite; + x = npy_creal@c@(z); y = npy_cimag@c@(z); - return npy_cpack@c@(npy_cos@c@(y)*npy_sinh@c@(x), npy_sin@c@(y)*npy_cosh@c@(x)); + + xfinite = npy_isfinite(x); + yfinite = npy_isfinite(y); + + /* Handle the nearly-non-exceptional cases where x and y are finite. */ + if (xfinite && yfinite) { + if (y == 0) + return npy_cpack@c@(npy_sinh@c@(x), y); + absx = npy_fabs@c@(x); + if (absx < CSINH_BIG) /* small x: normal case */ + return npy_cpack@c@(npy_sinh@c@(x) * npy_cos@c@(y), npy_cosh@c@(x) * npy_sin@c@(y)); + + /* |x| >= 22, so cosh(x) ~= exp(|x|) */ + if (absx < SCALED_CEXP_LOWER@C@) { + /* x < 710: exp(|x|) won't overflow */ + h = npy_exp@c@(npy_fabs@c@(x)) * 0.5; + return npy_cpack@c@(npy_copysign@c@(h, x) * npy_cos@c@(y), h * npy_sin@c@(y)); + } else if (x < SCALED_CEXP_UPPER@C@) { + /* x < 1455: scale to avoid overflow */ + z = _npy_scaled_cexp@c@(absx, y, -1); + return npy_cpack@c@(npy_creal@c@(z) * npy_copysign@c@(1, x), npy_cimag@c@(z)); + } else { + /* x >= 1455: the result always overflows */ + h = CSINH_HUGE * x; + return npy_cpack@c@(h * npy_cos@c@(y), h * h * npy_sin@c@(y)); + } + } + + /* + * sinh(+-0 +- I Inf) = sign(d(+-0, dNaN))0 + I dNaN. + * The sign of 0 in the result is unspecified. Choice = normally + * the same as dNaN. Raise the invalid floating-point exception. + * + * sinh(+-0 +- I NaN) = sign(d(+-0, NaN))0 + I d(NaN). + * The sign of 0 in the result is unspecified. Choice = normally + * the same as d(NaN). + */ + if (x == 0 && !yfinite) + return npy_cpack@c@(npy_copysign@c@(0, x * (y - y)), y - y); + + /* + * sinh(+-Inf +- I 0) = +-Inf + I +-0. + * + * sinh(NaN +- I 0) = d(NaN) + I +-0. + */ + if (y == 0 && !xfinite) { + if (npy_isnan(x)) + return z; + return npy_cpack@c@(x, npy_copysign@c@(0, y)); + } + + /* + * sinh(x +- I Inf) = dNaN + I dNaN. + * Raise the invalid floating-point exception for finite nonzero x. + * + * sinh(x + I NaN) = d(NaN) + I d(NaN). + * Optionally raises the invalid floating-point exception for finite + * nonzero x. Choice = don't raise (except for signaling NaNs). + */ + if (xfinite && !yfinite) + return npy_cpack@c@(y - y, x * (y - y)); + + /* + * sinh(+-Inf + I NaN) = +-Inf + I d(NaN). + * The sign of Inf in the result is unspecified. Choice = normally + * the same as d(NaN). + * + * sinh(+-Inf +- I Inf) = +Inf + I dNaN. + * The sign of Inf in the result is unspecified. Choice = always +. + * Raise the invalid floating-point exception. + * + * sinh(+-Inf + I y) = +-Inf cos(y) + I Inf sin(y) + */ + if (!xfinite && !npy_isnan(x)) { + if (!yfinite) + return npy_cpack@c@(x * x, x * (y - y)); + return npy_cpack@c@(x * npy_cos@c@(y), NPY_INFINITY@C@ * npy_sin@c@(y)); + } + + /* + * sinh(NaN + I NaN) = d(NaN) + I d(NaN). + * + * sinh(NaN +- I Inf) = d(NaN) + I d(NaN). + * Optionally raises the invalid floating-point exception. + * Choice = raise. + * + * sinh(NaN + I y) = d(NaN) + I d(NaN). + * Optionally raises the invalid floating-point exception for finite + * nonzero y. Choice = don't raise (except for signaling NaNs). + */ + return npy_cpack@c@((x * x) * (y - y), (x + x) * (y - y)); } +#undef CSINH_BIG +#undef CSINH_HUGE #endif #ifndef HAVE_CTANH@C@ From a8378532b2fcb394f59360ba3fe7be3459a84e57 Mon Sep 17 00:00:00 2001 From: Eric Moore Date: Wed, 9 Oct 2013 13:32:30 -0400 Subject: [PATCH 18/35] ENH: Import the catanh/catan implemenation from FreeBSD The code from FreeBSD was lightly adapted to fit with the numpy style. An incorrect test for the branch cuts of both arctanh and arctan was corrected in both test_umath.py and test_c99complex.c. With this commit, npy_catanh(f) and npy_catan(f) pass all of the tests in test_c99complex.c. --- numpy/core/src/npymath/npy_math_complex.c.src | 259 ++++++++++++++---- numpy/core/test_c99complex.c | 70 ++--- numpy/core/tests/test_umath.py | 8 +- 3 files changed, 242 insertions(+), 95 deletions(-) diff --git a/numpy/core/src/npymath/npy_math_complex.c.src b/numpy/core/src/npymath/npy_math_complex.c.src index 1e77184f20d0..8fcd4f5df088 100644 --- a/numpy/core/src/npymath/npy_math_complex.c.src +++ b/numpy/core/src/npymath/npy_math_complex.c.src @@ -7,6 +7,7 @@ * 2009), under the following license: * * Copyright (c) 2007, 2011 David Schultz + * Copyright (c) 2012 Stephen Montgomery-Smith * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -41,6 +42,7 @@ * #TMAX = FLT_MAX, DBL_MAX, LDBL_MAX# * #TMIN = FLT_MIN, DBL_MIN, LDBL_MIN# * #TMANT_DIG = FLT_MANT_DIG, DBL_MANT_DIG, LDBL_MANT_DIG# + * #TEPS = FLT_EPSILON, DBL_EPSILON, LDBL_EPSILON# * #precision = 1, 2, 3# */ @@ -194,7 +196,6 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) npy_ldexp@c@(mant * mantsin, expt + exsin)); } - #ifndef HAVE_CEXP@C@ @ctype@ npy_cexp@c@(@ctype@ z) { @@ -922,38 +923,9 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) #ifndef HAVE_CATAN@C@ @ctype@ npy_catan@c@(@ctype@ z) { - @type@ x, y; - x = npy_creal@c@(z); - y = npy_cimag@c@(z); - - if (npy_fabs(x) > 1e-3 || npy_fabs(y) > 1e-3) { - /* catan(z) = 0.5*i * log((i+z)/(i-z)) */ - @ctype@ ip, im; - ip = cadd@c@(c_i@c@, z); - im = csub@c@(c_i@c@, z); - return cmul@c@(c_ihalf@c@, npy_clog@c@(cdiv@c@(ip, im))); - } - else { - /* - * Small arguments: series expansion, to avoid loss of precision - * atan(x) = x [1 - (1/3) x^2 [1 - (3/5) x^2 [1 - ...]]] - * - * |x| < 1e-3 => |rel. error| < 1e-18 (f), 1e-24, 1e-36 (l) - */ - @ctype@ z2, r; - z2 = cmul@c@(z, z); - r = c_1@c@; -#if @precision@ >= 3 - SERIES_HORNER_TERM@C@(r, z2, -9.0@C@/11); - SERIES_HORNER_TERM@C@(r, z2, -7.0@C@/9); -#endif -#if @precision@ >= 2 - SERIES_HORNER_TERM@C@(r, z2, -5.0@C@/7); -#endif - SERIES_HORNER_TERM@C@(r, z2, -3.0@C@/5); - SERIES_HORNER_TERM@C@(r, z2, -1.0@C@/3); - return cmul@c@(r, z); - } + /* catan(z) = I * conj( catanh(I * conj(z)) ) */ + z = npy_catanh@c@(npy_cpack@c@(npy_cimag@c@(z), npy_creal@c@(z))); + return npy_cpack@c@(npy_cimag@c@(z), npy_creal@c@(z)); } #endif @@ -1007,41 +979,208 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) #endif #ifndef HAVE_CATANH@C@ +/* + * sum_squares(x,y) = x*x + y*y (or just x*x if y*y would underflow). + * Assumes x*x and y*y will not overflow. + * Assumes x and y are finite. + * Assumes y is non-negative. + * Assumes fabs(x) >= DBL_EPSILON. + */ +static inline @type@ _sum_squares@c@(@type@ x, @type@ y) +{ +#if @precision@ == 1 +const npy_float SQRT_MIN = 1.0842022e-19f; +#endif +#if @precision@ == 2 +const npy_double SQRT_MIN = 1.4916681462400413e-154; /* sqrt(DBL_MIN) */ +#endif +#if @precision@ == 3 +/* this is correct for 80 bit long doubles */ +const npy_longdouble SQRT_MIN = 1.8336038675548471656e-2466l; +#endif + /* Avoid underflow when y is small. */ + if (y < SQRT_MIN) + return (x * x); + + return (x * x + y * y); +} + +/* + * real_part_reciprocal(x, y) = Re(1/(x+I*y)) = x/(x*x + y*y). + * Assumes x and y are not NaN, and one of x and y is larger than + * RECIP_EPSILON. We avoid unwarranted underflow. It is important to not use + * the code creal(1/z), because the imaginary part may produce an unwanted + * underflow. + * This is only called in a context where inexact is always raised before + * the call, so no effort is made to avoid or force inexact. + */ +#if @precision@ == 1 +#define BIAS (FLT_MAX_EXP - 1) +#define CUTOFF (FLT_MANT_DIG / 2 + 1) +static inline npy_float _real_part_reciprocalf(npy_float x, npy_float y) +{ + npy_float scale; + npy_uint32 hx, hy; + npy_int32 ix, iy; + + GET_FLOAT_WORD(hx, x); + ix = hx & 0x7f800000; + GET_FLOAT_WORD(hy, y); + iy = hy & 0x7f800000; + if (ix - iy >= CUTOFF << 23 || npy_isinf(x)) + return (1 / x); + if (iy - ix >= CUTOFF << 23) + return (x / y / y); + if (ix <= (BIAS + FLT_MAX_EXP / 2 - CUTOFF) << 23) + return (x / (x * x + y * y)); + SET_FLOAT_WORD(scale, 0x7f800000 - ix); + x *= scale; + y *= scale; + return (x / (x * x + y * y) * scale); +} +#undef BIAS +#undef CUTOFF +#endif +#if @precision@ == 2 +#define BIAS (DBL_MAX_EXP - 1) +/* XXX more guard digits are useful iff there is extra precision. */ +#define CUTOFF (DBL_MANT_DIG / 2 + 1) /* just half or 1 guard digit */ +static inline npy_double _real_part_reciprocal(npy_double x, npy_double y) +{ + npy_double scale; + npy_uint32 hx, hy; + npy_int32 ix, iy; + + /* + * This code is inspired by the C99 document n1124.pdf, Section G.5.1, + * example 2. + */ + GET_HIGH_WORD(hx, x); + ix = hx & 0x7ff00000; + GET_HIGH_WORD(hy, y); + iy = hy & 0x7ff00000; + if (ix - iy >= CUTOFF << 20 || npy_isinf(x)) + return (1 / x); /* +-Inf -> +-0 is special */ + if (iy - ix >= CUTOFF << 20) + return (x / y / y); /* should avoid double div, but hard */ + if (ix <= (BIAS + DBL_MAX_EXP / 2 - CUTOFF) << 20) + return (x / (x * x + y * y)); + scale = 1; + SET_HIGH_WORD(scale, 0x7ff00000 - ix); /* 2**(1-ilogb(x)) */ + x *= scale; + y *= scale; + return (x / (x * x + y * y) * scale); +} +#undef BIAS +#undef CUTOFF +#endif +#if @precision@ == 3 +#define BIAS (LDBL_MAX_EXP - 1) +#define CUTOFF (LDBL_MANT_DIG / 2 + 1) +static inline npy_longdouble _real_part_reciprocall(npy_longdouble x, npy_longdouble y) +{ + npy_longdouble scale; + union IEEEl2bitsrep ux, uy, us; + npy_int32 ix, iy; + + ux.e = x; + ix = GET_LDOUBLE_EXP(ux); + uy.e = y; + iy = GET_LDOUBLE_EXP(uy); + if (ix - iy >= CUTOFF || npy_isinf(x)) + return (1/x); + if (iy - ix >= CUTOFF) + return (x/y/y); + if (ix <= BIAS + LDBL_MAX_EXP / 2 - CUTOFF) + return (x/(x*x + y*y)); + us.e = 1; + SET_LDOUBLE_EXP(us, 0x7fff - ix); + scale = us.e; + x *= scale; + y *= scale; + return (x/(x*x + y*y) * scale); +} +#undef BIAS +#undef CUTOFF +#endif + @ctype@ npy_catanh@c@(@ctype@ z) { - @type@ x, y; +#if @precision@ == 1 + /* this is sqrt(3*EPS) */ + const npy_float SQRT_3_EPSILON = 5.9801995673e-4f; + /* chosen such that pio2_hi + pio2_lo == pio2_hi but causes FE_INEXACT. */ + const volatile float pio2_lo = 7.5497899549e-9f; +#endif +#if @precision@ == 2 + const npy_double SQRT_3_EPSILON = 2.5809568279517849e-8; + const volatile npy_double pio2_lo = 6.1232339957367659e-17; +#endif +#if @precision@ == 3 + const npy_longdouble SQRT_3_EPSILON = 5.70316273435758915310e-10; + const volatile npy_longdouble pio2_lo = 2.710505431213761085e-20l; +#endif + const @type@ RECIP_EPSILON = 1.0@c@ / @TEPS@; + const @type@ pio2_hi = NPY_PI_2@c@; + const volatile float tiny = 3.9443045e-31f; + @type@ x, y, ax, ay, rx, ry; x = npy_creal@c@(z); y = npy_cimag@c@(z); - - if (npy_fabs(x) > 1e-3 || npy_fabs(y) > 1e-3) { - /* catanh(z) = 0.5 * log((1+z)/(1-z)) */ - @ctype@ p1, m1; - p1 = cadd@c@(c_1@c@, z); - m1 = csub@c@(c_1@c@, z); - return cmul@c@(c_half@c@, npy_clog@c@(cdiv@c@(p1, m1))); + ax = npy_fabs@c@(x); + ay = npy_fabs@c@(y); + + /* This helps handle many cases. */ + if (y == 0 && ax <= 1) + return npy_cpack@c@(npy_atanh@c@(x), y); + + /* To ensure the same accuracy as atan(), and to filter out z = 0. */ + if (x == 0) + return npy_cpack@c@(x, npy_atan@c@(y)); + + if (npy_isnan(x) || npy_isnan(y)) { + /* catanh(+-Inf + I*NaN) = +-0 + I*NaN */ + if (npy_isinf(x)) + return npy_cpack@c@(npy_copysign@c@(0, x), y + y); + /* catanh(NaN + I*+-Inf) = sign(NaN)0 + I*+-PI/2 */ + if (npy_isinf(y)) + return npy_cpack@c@(npy_copysign@c@(0, x), + npy_copysign@c@(pio2_hi + pio2_lo, y)); + /* + * All other cases involving NaN return NaN + I*NaN. + * C99 leaves it optional whether to raise invalid if one of + * the arguments is not NaN, so we opt not to raise it. + */ + return npy_cpack@c@(x + 0.0L + (y + 0), x + 0.0L + (y + 0)); } - else { + + if (ax > RECIP_EPSILON || ay > RECIP_EPSILON) + return npy_cpack@c@(_real_part_reciprocal@c@(x, y), + npy_copysign@c@(pio2_hi + pio2_lo, y)); + + if (ax < SQRT_3_EPSILON / 2 && ay < SQRT_3_EPSILON / 2) { /* - * Small arguments: series expansion, to avoid loss of precision - * atan(x) = x [1 + (1/3) x^2 [1 + (3/5) x^2 [1 + ...]]] - * - * |x| < 1e-3 => |rel. error| < 1e-18 (f), 1e-24, 1e-36 (l) + * z = 0 was filtered out above. All other cases must raise + * inexact, but this is the only only that needs to do it + * explicitly. */ - @ctype@ z2, r; - z2 = cmul@c@(z, z); - r = c_1@c@; -#if @precision@ >= 3 - SERIES_HORNER_TERM@C@(r, z2, 9.0@C@/11); - SERIES_HORNER_TERM@C@(r, z2, 7.0@C@/9); -#endif -#if @precision@ >= 2 - SERIES_HORNER_TERM@C@(r, z2, 5.0@C@/7); -#endif - SERIES_HORNER_TERM@C@(r, z2, 3.0@C@/5); - SERIES_HORNER_TERM@C@(r, z2, 1.0@C@/3); - return cmul@c@(z, r); - } + volatile npy_float junk = 1 + tiny; + return (z); + } + + if (ax == 1 && ay < @TEPS@) + rx = (NPY_LOGE2@c@ - npy_log@c@(ay)) / 2; + else + rx = npy_log1p@c@(4 * ax / _sum_squares@c@(ax - 1, ay)) / 4; + + if (ax == 1) + ry = npy_atan2@c@(2, -ay) / 2; + else if (ay < @TEPS@) + ry = npy_atan2@c@(2 * ay, (1 - ax) * (1 + ax)) / 2; + else + ry = npy_atan2@c@(2 * ay, (1 - ax) * (1 + ax) - ay * ay) / 2; + + return npy_cpack@c@(npy_copysign@c@(rx, x), npy_copysign@c@(ry, y)); } #endif /**end repeat**/ diff --git a/numpy/core/test_c99complex.c b/numpy/core/test_c99complex.c index 09f11affb023..79eb15ad5359 100644 --- a/numpy/core/test_c99complex.c +++ b/numpy/core/test_c99complex.c @@ -91,7 +91,7 @@ const TYPE NZERO = -1.0 * 0.0; #define TEST_CC(func, xr, xi, er, ei) \ TEST_INT(func, xr, xi, er, ei, isclose, isclose) -#define TEST_UNSPECIFIED2(func, xr, xi, er1, ei1, er2, ei2) \ +#define TEST_UNSPECIFIED2_INT(func, xr, xi, er1, ei1, er2, ei2, rtest, itest) \ do { \ TYPE dxr = xr; \ TYPE dxi = xi; \ @@ -103,8 +103,8 @@ const TYPE NZERO = -1.0 * 0.0; TYPE complex r = ADDSUFFIX(func)(x); \ TYPE rr = ADDSUFFIX(creal)(r); \ TYPE ri = ADDSUFFIX(cimag)(r); \ - if (!((isequal(rr, der1) && isequal(ri, dei1)) || \ - (isequal(rr, der2) && isequal(ri, dei2)))) { \ + if (!((rtest(rr, der1) && itest(ri, dei1)) || \ + (rtest(rr, der2) && itest(ri, dei2)))) { \ ret = 0; \ TEST_PRINTF(func, dxr, dxi, der1, dei1, rr, ri); \ printf("or"); \ @@ -113,6 +113,15 @@ const TYPE NZERO = -1.0 * 0.0; } \ while(0) +#define TEST_UNSPECIFIED2(func, xr, xi, er1, ei1, er2, ei2) \ + TEST_UNSPECIFIED2_INT(func, xr, xi, er1, ei1, er2, ei2, isequal, isequal) \ + +#define TEST_UNSPECIFIED2_CE(func, xr, xi, er1, ei1, er2, ei2) \ + TEST_UNSPECIFIED2_INT(func, xr, xi, er1, ei1, er2, ei2, isclose, isequal) \ + +#define TEST_UNSPECIFIED2_EC(func, xr, xi, er1, ei1, er2, ei2) \ + TEST_UNSPECIFIED2_INT(func, xr, xi, er1, ei1, er2, ei2, isequal, isclose) \ + #define TEST_UNSPECIFIED4(func, xr, xi, er1, ei1, er2, ei2, er3, ei3, er4, ei4)\ do { \ TYPE dxr = xr; \ @@ -309,7 +318,7 @@ int check_branch_cut(complexfunc cfunc, TYPE complex x0, TYPE complex dx, { const TYPE scale = EPS * 1e2; const TYPE atol = 1e-4; - + TYPE complex shift = dx*scale*ADDSUFFIX(cabs)(x0)/ADDSUFFIX(cabs)(dx); TYPE complex y0 = cfunc(x0); TYPE complex yp = cfunc(x0 + shift); @@ -420,7 +429,7 @@ int clp_internal(complexfunc cfunc, realfunc rfunc, int real, TYPE x) } return ADDSUFFIX(fabs)(num/den - 1); } - + int check_loss_of_precision(complexfunc cfunc, realfunc rfunc, int real, const char* fname) { @@ -439,7 +448,7 @@ int check_loss_of_precision(complexfunc cfunc, realfunc rfunc, int real, TYPE x, ratio; int k; int ret = 1; - + for(k = 0; k < n_series; k++) { x = ADDSUFFIX(pow)(10.0, xsb + k*dxs); ratio = clp_internal(cfunc, rfunc, real, x); @@ -479,7 +488,7 @@ int test_cacos() TEST_CE(cacos, 0, NAN, NPY_PI_2, NAN); TEST_CE(cacos, NZERO, NAN, NPY_PI_2, NAN); - + TEST_CE(cacos, 2.0, INFINITY, NPY_PI_2, -INFINITY); TEST_CE(cacos, 2.0, -INFINITY, NPY_PI_2, INFINITY); @@ -517,7 +526,7 @@ int test_cacos() TEST_BRANCH_CUT(cacos, 0, 2, 1, 0, 1, 1, 1); TEST_CC(cacos, 0.5, 0.0, ADDSUFFIX(acos)(0.5), 0.0); - + return ret; } #endif @@ -557,7 +566,7 @@ int test_casin() TEST_EE(casin, 0, NAN, 0, NAN); TEST_EE(casin, NZERO, NAN, NZERO, NAN); - + /* can raise FE_INVALID or not */ TEST_EE(casin, -2.0, NAN, NAN, NAN); TEST_EE(casin, 2.0, NAN, NAN, NAN); @@ -567,7 +576,7 @@ int test_casin() TEST_UNSPECIFIED2(casin, INFINITY, NAN, NAN, INFINITY, NAN, -INFINITY); TEST_EE(casin, NAN, NAN, NAN, NAN); - + TEST_LOSS_OF_PRECISION(casin, asin, 0); TEST_CC(casin, 1e-5, 1e-5, 9.999999999666666667e-6, 1.0000000000333333333e-5); @@ -592,7 +601,7 @@ int test_catan() TEST_EE(catan, 0, NZERO, 0, NZERO); TEST_EE(catan, NZERO, 0, NZERO, 0); TEST_EE(catan, NZERO, NZERO, NZERO, NZERO); - + TEST_EE(catan, NAN, 0, NAN, 0); TEST_EE(catan, NAN, NZERO, NAN, NZERO); @@ -628,8 +637,8 @@ int test_catan() TEST_EE(catan, 2.0, NAN, NAN, NAN); /* sign of real part is unspecified */ - TEST_UNSPECIFIED2(catan, -INFINITY, NAN, -NPY_PI_2, 0, -NPY_PI_2, NZERO); - TEST_UNSPECIFIED2(catan, INFINITY, NAN, NPY_PI_2, 0, NPY_PI_2, NZERO); + TEST_UNSPECIFIED2_CE(catan, -INFINITY, NAN, -NPY_PI_2, 0, -NPY_PI_2, NZERO); + TEST_UNSPECIFIED2_CE(catan, INFINITY, NAN, NPY_PI_2, 0, NPY_PI_2, NZERO); TEST_EE(catan, NAN, NAN, NAN, NAN); @@ -638,7 +647,7 @@ int test_catan() TEST_CC(catan, 1e-5, 1e-5, 1.000000000066666666e-5, 9.999999999333333333e-6); TEST_BRANCH_CUT(catan, 0, -2, 1, 0, -1, 1, 1); - TEST_BRANCH_CUT(catan, 0, 2, -1, 0, -1, 1, 1); + TEST_BRANCH_CUT(catan, 0, 2, 1, 0, -1, 1, 1); TEST_BRANCH_CUT(catan, -2, 0, 0, 1, 1, 1, 1); TEST_BRANCH_CUT(catan, 2, 0, 0, 1, 1, 1, 1); @@ -673,7 +682,7 @@ int test_cacosh() TEST_EC(cacosh, -INFINITY, INFINITY, INFINITY, 0.75*NPY_PI); TEST_EC(cacosh, -INFINITY, -INFINITY, INFINITY, -0.75*NPY_PI); - + TEST_EC(cacosh, INFINITY, INFINITY, INFINITY, 0.25*NPY_PI); TEST_EC(cacosh, INFINITY, -INFINITY, INFINITY, -0.25*NPY_PI); @@ -731,10 +740,10 @@ int test_casinh() TEST_EE(casinh, INFINITY, NAN, INFINITY, NAN); TEST_EE(casinh, -INFINITY, NAN, -INFINITY, NAN); - + TEST_EE(casinh, NAN, 0, NAN, 0); TEST_EE(casinh, NAN, NZERO, NAN, NZERO); - + /* can raise FE_INVALID or not */ TEST_EE(casinh, NAN, 2.0, NAN, NAN); TEST_EE(casinh, NAN, -2.0, NAN, NAN); @@ -770,7 +779,7 @@ int test_catanh() TEST_EE(catanh, 0, NZERO, 0, NZERO); TEST_EE(catanh, NZERO, 0, NZERO, 0); TEST_EE(catanh, NZERO, NZERO, NZERO, NZERO); - + TEST_EE(catanh, 0, NAN, 0, NAN); TEST_EE(catanh, NZERO, NAN, NZERO, NAN); @@ -806,10 +815,9 @@ int test_catanh() TEST_EE(catanh, NAN, -2.0, NAN, NAN); /* sign of real part is unspecified */ - TEST_UNSPECIFIED2(catanh, NAN, INFINITY, 0, NPY_PI_2, NZERO, NPY_PI_2); - TEST_UNSPECIFIED2(catanh, NAN, -INFINITY, 0, -NPY_PI_2, NZERO, -NPY_PI_2); + TEST_UNSPECIFIED2_EC(catanh, NAN, INFINITY, 0, NPY_PI_2, NZERO, NPY_PI_2); + TEST_UNSPECIFIED2_EC(catanh, NAN, -INFINITY, 0, -NPY_PI_2, NZERO, -NPY_PI_2); - /* TEST(catanh, NAN, INFINITY, 0, NPY_PI_2); */ TEST_EE(catanh, NAN, NAN, NAN, NAN); TEST_LOSS_OF_PRECISION(catanh, atanh, 1); @@ -817,7 +825,7 @@ int test_catanh() TEST_CC(catanh, 1e-5, 1e-5, 9.999999999333333333e-6, 1.000000000066666666e-5); TEST_BRANCH_CUT(catanh, -2, 0, 0, 1, 1, -1, 1); - TEST_BRANCH_CUT(catanh, 2, 0, 0, -1, 1, -1, 1); + TEST_BRANCH_CUT(catanh, 2, 0, 0, 1, 1, -1, 1); TEST_BRANCH_CUT(catanh, 0, -2, 1, 0, 1, 1, 1); TEST_BRANCH_CUT(catanh, 0, 2, 1, 0, 1, 1, 1); TEST_BRANCH_CUT(catanh, 0, 0, 0, 1, 1, 1, 1); @@ -967,7 +975,7 @@ int test_csin() TEST_EE(csin, NAN, NAN, NAN, NAN); TEST_CC(csin, 0.5, 0, ADDSUFFIX(sin)(0.5), 0); - + return ret; } #endif @@ -1165,7 +1173,7 @@ int test_csinh() TEST_EE(csinh, NAN, NAN, NAN, NAN); TEST_CC(csinh, 0.5, 0, ADDSUFFIX(sinh)(0.5), 0); - + return ret; } #endif @@ -1281,7 +1289,7 @@ int test_cexp() TEST_EE(cexp, NAN, 0, NAN, 0); TEST_EE(cexp, NAN, NZERO, NAN, NZERO); - + /* can raise FE_INVALID or not */ TEST_EE(cexp, NAN, 2.0, NAN, NAN); TEST_EE(cexp, NAN, -2.0, NAN, NAN); @@ -1317,7 +1325,7 @@ int test_clog() TEST_EC(clog, -INFINITY, 2.0, INFINITY, NPY_PI); TEST_EC(clog, -INFINITY, -2.0, INFINITY, -NPY_PI); - + TEST_EE(clog, INFINITY, 2.0, INFINITY, 0); TEST_EE(clog, INFINITY, -2.0, INFINITY, NZERO); @@ -1338,7 +1346,7 @@ int test_clog() TEST_EE(clog, NAN, -INFINITY, INFINITY, NAN); TEST_EE(clog, NAN, NAN, NAN, NAN); - + TEST_BRANCH_CUT(clog, -0.5, 0, 0, 1, 1, -1, 1); TEST_CC(clog, 0.5, 0, ADDSUFFIX(log)(0.5), 0); @@ -1390,7 +1398,7 @@ int test_cpow() TEST_CPOW_CC(1, 2, -3, 0, -11.0/125.0, 2.0/125.0); TEST_CPOW_CC(2, 3, -3, 0, -46.0/2197.0, -9.0/2197.0); TEST_CPOW_CC(3, 4, -3, 0, -117.0/15625.0, -44.0/15625.0); - + TEST_CPOW_CC(1, 2, 0.5, 0, 1.272019649514069, 0.7861513777574233); TEST_CPOW_CC(2, 3, 0.5, 0, 1.6741492280355401, 0.895977476129838); TEST_CPOW_CC(3, 4, 0.5, 0, 2, 1); @@ -1406,7 +1414,7 @@ int test_cpow() TEST_CPOW_EE(1, INFINITY, 1, 0, 1, INFINITY); TEST_CPOW_EE(1, INFINITY, 2, 0, -INFINITY, INFINITY); TEST_CPOW_EE(1, INFINITY, 3, 0, -INFINITY, NAN); - + /* tests from test_umath.py: TestPower: test_power_zero */ TEST_CPOW_CC(0, 0, 0.33, 0, 0, 0); TEST_CPOW_CC(0, 0, 0.5, 0, 0, 0); @@ -1494,7 +1502,7 @@ int test_csqrt() TEST_EE(csqrt, NAN, INFINITY, INFINITY, INFINITY); TEST_EE(csqrt, NAN, -INFINITY, INFINITY, -INFINITY); - + TEST_EE(csqrt, INFINITY, INFINITY, INFINITY, INFINITY); TEST_EE(csqrt, INFINITY, -INFINITY, INFINITY, -INFINITY); @@ -1509,7 +1517,7 @@ int test_csqrt() TEST_EE(csqrt, INFINITY, 2.0, INFINITY, 0); TEST_EE(csqrt, INFINITY, -2.0, INFINITY, NZERO); - + /* sign of imaginary part is unspecified */ TEST_UNSPECIFIED2(csqrt, -INFINITY, NAN, NAN, INFINITY, NAN, -INFINITY); diff --git a/numpy/core/tests/test_umath.py b/numpy/core/tests/test_umath.py index 6db95e9a6dff..38ab81006ea4 100644 --- a/numpy/core/tests/test_umath.py +++ b/numpy/core/tests/test_umath.py @@ -1122,11 +1122,11 @@ def test_branch_cuts(self): yield _check_branch_cut, np.arcsin, [ -2, 2], [1j, -1j], 1, -1 yield _check_branch_cut, np.arccos, [ -2, 2], [1j, -1j], 1, -1 - yield _check_branch_cut, np.arctan, [-2j, 2j], [1, -1 ], -1, 1 + yield _check_branch_cut, np.arctan, [-2j, 2j], [1, 1 ], -1, 1 yield _check_branch_cut, np.arcsinh, [-2j, 2j], [-1, 1], -1, 1 yield _check_branch_cut, np.arccosh, [ -1, 0.5], [1j, 1j], 1, -1 - yield _check_branch_cut, np.arctanh, [ -2, 2], [1j, -1j], 1, -1 + yield _check_branch_cut, np.arctanh, [ -2, 2], [1j, 1j], 1, -1 # check against bogus branch cuts: assert continuity between quadrants yield _check_branch_cut, np.arcsin, [-2j, 2j], [ 1, 1], 1, 1 @@ -1149,10 +1149,10 @@ def test_branch_cuts_failing(self): yield _check_branch_cut, np.sqrt, -0.5, 1j, 1, -1, True yield _check_branch_cut, np.arcsin, [ -2, 2], [1j, -1j], 1, -1, True yield _check_branch_cut, np.arccos, [ -2, 2], [1j, -1j], 1, -1, True - yield _check_branch_cut, np.arctan, [-2j, 2j], [1, -1 ], -1, 1, True + yield _check_branch_cut, np.arctan, [-2j, 2j], [1, 1 ], -1, 1, True yield _check_branch_cut, np.arcsinh, [-2j, 2j], [-1, 1], -1, 1, True yield _check_branch_cut, np.arccosh, [ -1, 0.5], [1j, 1j], 1, -1, True - yield _check_branch_cut, np.arctanh, [ -2, 2], [1j, -1j], 1, -1, True + yield _check_branch_cut, np.arctanh, [ -2, 2], [1j, 1j], 1, -1, True def test_against_cmath(self): import cmath, sys From 36e0d8b8ebbd07441a9d1bb36413f7fdf60f19d3 Mon Sep 17 00:00:00 2001 From: Eric Moore Date: Wed, 9 Oct 2013 15:11:04 -0400 Subject: [PATCH 19/35] BUG: printf requires a literal for the first argument. Not sure why this shows up as a warning on py2.x but as an error on py3.x. --- numpy/core/test_c99complex.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/numpy/core/test_c99complex.c b/numpy/core/test_c99complex.c index 79eb15ad5359..2c1e0047c4bc 100644 --- a/numpy/core/test_c99complex.c +++ b/numpy/core/test_c99complex.c @@ -395,8 +395,7 @@ int check_near_crossover(complexfunc cfunc, const char* fname) diff = ADDSUFFIX(cabs)(czp - czm); if ( diff > 2*EPS || czp == czm) { - printf(fname); - printf(": Loss of precision: j = %d, k = %d\n", j, k); + printf("%s: Loss of precision: j = %d, k = %d\n",fname, j, k); printf("zp = (" FMT " + " FMT "j) -> (" FMT " + " FMT "j)\n", \ ADDSUFFIX(creal)(zp), ADDSUFFIX(cimag)(zp), \ ADDSUFFIX(creal)(czp), ADDSUFFIX(cimag)(czp)); @@ -453,8 +452,7 @@ int check_loss_of_precision(complexfunc cfunc, realfunc rfunc, int real, x = ADDSUFFIX(pow)(10.0, xsb + k*dxs); ratio = clp_internal(cfunc, rfunc, real, x); if (ratio > rtol) { - printf(fname); - printf(": Loss of precision vs real:\n"); + printf("%s: Loss of precision vs real:\n", fname); printf("x = " FMT "\n", x); printf("ratio = " FMT "\n", ratio); ret = 0; @@ -465,8 +463,7 @@ int check_loss_of_precision(complexfunc cfunc, realfunc rfunc, int real, x = ADDSUFFIX(pow)(10.0, xbb + k*dxb); ratio = clp_internal(cfunc, rfunc, real, x); if (ratio > rtol) { - printf(fname); - printf(": Loss of precision vs. real:\n"); + printf("%s: Loss of precision vs. real:\n", fname); printf("x = " FMT "\n", x); printf("ratio = " FMT "\n", ratio); ret = 0; From 330b54aac310f4419162c744638358326fc66acd Mon Sep 17 00:00:00 2001 From: Eric Moore Date: Wed, 9 Oct 2013 16:07:12 -0400 Subject: [PATCH 20/35] BUG: Py3 '-2j' is (NZERO - 2j) causes test failures On python2.x evaluating '-2j' produces complex(0,-2), however, on python3, it instead produces complex(NZERO, -2). This ends up causing a test for the complex arctan to fail. This happens I suppose for the same reason it does in C. The expression -2j ends up being evaluated as (-2 + 0j) * (0 + 1j) == (-0 + -2j). --- numpy/core/tests/test_umath.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/numpy/core/tests/test_umath.py b/numpy/core/tests/test_umath.py index 38ab81006ea4..f747aa89c290 100644 --- a/numpy/core/tests/test_umath.py +++ b/numpy/core/tests/test_umath.py @@ -1120,22 +1120,22 @@ def test_branch_cuts(self): yield _check_branch_cut, np.log1p, -1.5, 1j, 1, -1 yield _check_branch_cut, np.sqrt, -0.5, 1j, 1, -1 - yield _check_branch_cut, np.arcsin, [ -2, 2], [1j, -1j], 1, -1 - yield _check_branch_cut, np.arccos, [ -2, 2], [1j, -1j], 1, -1 - yield _check_branch_cut, np.arctan, [-2j, 2j], [1, 1 ], -1, 1 + yield _check_branch_cut, np.arcsin, [ -2, 2], [1j, 0-1j], 1, -1 + yield _check_branch_cut, np.arccos, [ -2, 2], [1j, 0-1j], 1, -1 + yield _check_branch_cut, np.arctan, [0-2j, 2j], [1, 1 ], -1, 1 - yield _check_branch_cut, np.arcsinh, [-2j, 2j], [-1, 1], -1, 1 + yield _check_branch_cut, np.arcsinh, [0-2j, 2j], [-1, 1], -1, 1 yield _check_branch_cut, np.arccosh, [ -1, 0.5], [1j, 1j], 1, -1 yield _check_branch_cut, np.arctanh, [ -2, 2], [1j, 1j], 1, -1 # check against bogus branch cuts: assert continuity between quadrants - yield _check_branch_cut, np.arcsin, [-2j, 2j], [ 1, 1], 1, 1 - yield _check_branch_cut, np.arccos, [-2j, 2j], [ 1, 1], 1, 1 + yield _check_branch_cut, np.arcsin, [0-2j, 2j], [ 1, 1], 1, 1 + yield _check_branch_cut, np.arccos, [0-2j, 2j], [ 1, 1], 1, 1 yield _check_branch_cut, np.arctan, [ -2, 2], [1j, 1j], 1, 1 yield _check_branch_cut, np.arcsinh, [ -2, 2, 0], [1j, 1j, 1 ], 1, 1 - yield _check_branch_cut, np.arccosh, [-2j, 2j, 2], [1, 1, 1j], 1, 1 - yield _check_branch_cut, np.arctanh, [-2j, 2j, 0], [1, 1, 1j], 1, 1 + yield _check_branch_cut, np.arccosh, [0-2j, 2j, 2], [1, 1, 1j], 1, 1 + yield _check_branch_cut, np.arctanh, [0-2j, 2j, 0], [1, 1, 1j], 1, 1 @dec.knownfailureif(True, "These branch cuts are known to fail") def test_branch_cuts_failing(self): @@ -1149,8 +1149,8 @@ def test_branch_cuts_failing(self): yield _check_branch_cut, np.sqrt, -0.5, 1j, 1, -1, True yield _check_branch_cut, np.arcsin, [ -2, 2], [1j, -1j], 1, -1, True yield _check_branch_cut, np.arccos, [ -2, 2], [1j, -1j], 1, -1, True - yield _check_branch_cut, np.arctan, [-2j, 2j], [1, 1 ], -1, 1, True - yield _check_branch_cut, np.arcsinh, [-2j, 2j], [-1, 1], -1, 1, True + yield _check_branch_cut, np.arctan, [0-2j, 2j], [1, 1 ], -1, 1, True + yield _check_branch_cut, np.arcsinh, [0-2j, 2j], [-1, 1], -1, 1, True yield _check_branch_cut, np.arccosh, [ -1, 0.5], [1j, 1j], 1, -1, True yield _check_branch_cut, np.arctanh, [ -2, 2], [1j, 1j], 1, -1, True From def6327a8bc66658b98e518fff884fbd61060f48 Mon Sep 17 00:00:00 2001 From: Eric Moore Date: Thu, 10 Oct 2013 10:57:42 -0400 Subject: [PATCH 21/35] ENH: Import the cacos,casin,cacosh,casinh implemenation from FreeBSD The code from FreeBSD was lightly adapted to fit with the numpy style. Several incorrect branch cut tests where fixed. (All of these for all of the arc* functions that were changed have to do with getting the correct branch cut for (x,0) or (0,x).) With this commit npy_cacos(f), npy_casin(f), npy_cacosh(f), and npy_casinh(f) pass all of the tests in test_c99complex.c. --- numpy/core/src/npymath/npy_math_complex.c.src | 521 +++++++++++++++--- numpy/core/test_c99complex.c | 36 +- numpy/core/tests/test_umath.py | 12 +- 3 files changed, 459 insertions(+), 110 deletions(-) diff --git a/numpy/core/src/npymath/npy_math_complex.c.src b/numpy/core/src/npymath/npy_math_complex.c.src index 8fcd4f5df088..7c6d2f499ddf 100644 --- a/numpy/core/src/npymath/npy_math_complex.c.src +++ b/numpy/core/src/npymath/npy_math_complex.c.src @@ -34,6 +34,9 @@ #include "npy_math_common.h" #include "npy_math_private.h" +static npy_float tiny = 3.9443045e-31f; +#define raise_inexact() do { volatile npy_float junk = 1 + tiny; } while(0) + /**begin repeat * #type = npy_float, npy_double, npy_longdouble# * #ctype = npy_cfloat,npy_cdouble,npy_clongdouble# @@ -56,7 +59,7 @@ static const @ctype@ c_ihalf@c@ = {0.0, 0.5@C@}; /*========================================================== * Helper functions - * + * * These are necessary because we do not count on using a * C99 compiler. *=========================================================*/ @@ -109,7 +112,7 @@ static NPY_INLINE @ctype@ cdiv@c@(@ctype@ a, @ctype@ b) return npy_cpack@c@((ar*rat + ai)*scl, (ai*rat - ar)*scl); } } - + static NPY_INLINE @ctype@ cneg@c@(@ctype@ a) { return npy_cpack@c@(-npy_creal@c@(a), -npy_cimag@c@(a)); @@ -120,17 +123,6 @@ static NPY_INLINE @ctype@ cmuli@c@(@ctype@ a) return npy_cpack@c@(-npy_cimag@c@(a), npy_creal@c@(a)); } -/* - * Perform the operation result := 1 + coef * x * result, - * with real coefficient `coef`. - */ -#define SERIES_HORNER_TERM@C@(r, x, c) \ - do { \ - r = cmul@c@(r, x); \ - r = npy_cpack@c@(c*npy_creal@c@(r), c*npy_cimag@c@(r)); \ - r = cadd@c@(c_1@c@, r); \ - } while(0) - /*========================================================== * Custom implementation of missing complex C99 functions *=========================================================*/ @@ -864,59 +856,348 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) #undef TANHL_HUGE #endif -#ifndef HAVE_CACOS@C@ -@ctype@ npy_cacos@c@(@ctype@ z) +#if !defined (HAVE_CACOS@C@) || !defined (HAVE_CASINH@C@) +/* + * Complex inverse trig functions taken from the msum library in FreeBSD + * revision 251404 + * + * The algorithm is very close to that in "Implementing the complex arcsine + * and arccosine functions using exception handling" by T. E. Hull, Thomas F. + * Fairgrieve, and Ping Tak Peter Tang, published in ACM Transactions on + * Mathematical Software, Volume 23 Issue 3, 1997, Pages 299-335, + * http://dl.acm.org/citation.cfm?id=275324. + * + * Throughout we use the convention z = x + I*y. + * + * casinh(z) = sign(x)*log(A+sqrt(A*A-1)) + I*asin(B) + * where + * A = (|z+I| + |z-I|) / 2 + * B = (|z+I| - |z-I|) / 2 = y/A + * + * These formulas become numerically unstable: + * (a) for Re(casinh(z)) when z is close to the line segment [-I, I] (that + * is, Re(casinh(z)) is close to 0); + * (b) for Im(casinh(z)) when z is close to either of the intervals + * [I, I*infinity) or (-I*infinity, -I] (that is, |Im(casinh(z))| is + * close to PI/2). + * + * These numerical problems are overcome by defining + * f(a, b) = (hypot(a, b) - b) / 2 = a*a / (hypot(a, b) + b) / 2 + * Then if A < A_crossover, we use + * log(A + sqrt(A*A-1)) = log1p((A-1) + sqrt((A-1)*(A+1))) + * A-1 = f(x, 1+y) + f(x, 1-y) + * and if B > B_crossover, we use + * asin(B) = atan2(y, sqrt(A*A - y*y)) = atan2(y, sqrt((A+y)*(A-y))) + * A-y = f(x, y+1) + f(x, y-1) + * where without loss of generality we have assumed that x and y are + * non-negative. + * + * Much of the difficulty comes because the intermediate computations may + * produce overflows or underflows. This is dealt with in the paper by Hull + * et al by using exception handling. We do this by detecting when + * computations risk underflow or overflow. The hardest part is handling the + * underflows when computing f(a, b). + * + * Note that the function f(a, b) does not appear explicitly in the paper by + * Hull et al, but the idea may be found on pages 308 and 309. Introducing the + * function f(a, b) allows us to concentrate many of the clever tricks in this + * paper into one function. + */ + +/* + * Function f(a, b, hypot_a_b) = (hypot(a, b) - b) / 2. + * Pass hypot(a, b) as the third argument. + */ +static inline @type@ _f@c@(@type@ a, @type@ b, @type@ hypot_a_b) { - @ctype@ r; - r = cmul@c@(z,z); - r = csub@c@(c_1@c@, r); - r = npy_csqrt@c@(r); - r = cmuli@c@(r); - r = cadd@c@(z, r); - r = npy_clog@c@(r); - r = cmuli@c@(r); - r = cneg@c@(r); - return r; + if (b < 0) + return ((hypot_a_b - b) / 2); + if (b == 0) + return (a / 2); + return (a * a / (hypot_a_b + b) / 2); } -#endif -#ifndef HAVE_CASIN@C@ -@ctype@ npy_casin@c@(@ctype@ z) +/* + * All the hard work is contained in this function. + * x and y are assumed positive or zero, and less than RECIP_EPSILON. + * Upon return: + * rx = Re(casinh(z)) = -Im(cacos(y + I*x)). + * B_is_usable is set to 1 if the value of B is usable. + * If B_is_usable is set to 0, sqrt_A2my2 = sqrt(A*A - y*y), and new_y = y. + * If returning sqrt_A2my2 has potential to result in an underflow, it is + * rescaled, and new_y is similarly rescaled. + */ +static inline void _do_hard_work@c@(@type@ x, @type@ y, @type@ *rx, + npy_int *B_is_usable, @type@ *B, @type@ *sqrt_A2my2, @type@ *new_y) { - @type@ x, y; - x = npy_creal@c@(z); - y = npy_cimag@c@(z); +#if @precision@ == 1 + const npy_float A_crossover = 10.0f; + const npy_float B_crossover = 0.6417f; + const npy_float FOUR_SQRT_MIN = 0x1p-61f; +#endif +#if @precision@ == 2 + const npy_double A_crossover = 10.0; + const npy_double B_crossover = 0.6417; + const npy_double FOUR_SQRT_MIN = 0x1p-509; +#endif +#if @precision@ == 3 + const npy_longdouble A_crossover = 10.0l; + const npy_longdouble B_crossover = 0.6417l; + const npy_longdouble FOUR_SQRT_MIN = 0x1p-8189l; +#endif + @type@ R, S, A; /* A, B, R, and S are as in Hull et al. */ + @type@ Am1, Amy; /* A-1, A-y. */ + + R = npy_hypot@c@(x, y + 1); /* |z+I| */ + S = npy_hypot@c@(x, y - 1); /* |z-I| */ + + /* A = (|z+I| + |z-I|) / 2 */ + A = (R + S) / 2; + /* + * Mathematically A >= 1. There is a small chance that this will not + * be so because of rounding errors. So we will make certain it is + * so. + */ + if (A < 1) + A = 1; + + if (A < A_crossover) { + /* + * Am1 = fp + fm, where fp = f(x, 1+y), and fm = f(x, 1-y). + * rx = log1p(Am1 + sqrt(Am1*(A+1))) + */ + if (y == 1 && x < @TEPS@ * @TEPS@ / 128) { + /* + * fp is of order x^2, and fm = x/2. + * A = 1 (inexactly). + */ + *rx = npy_sqrt@c@(x); + } else if (x >= @TEPS@ * npy_fabs@c@(y - 1)) { + /* + * Underflow will not occur because + * x >= DBL_EPSILON^2/128 >= FOUR_SQRT_MIN + */ + Am1 = _f@c@(x, 1 + y, R) + _f@c@(x, 1 - y, S); + *rx = npy_log1p@c@(Am1 + npy_sqrt@c@(Am1 * (A + 1))); + } else if (y < 1) { + /* + * fp = x*x/(1+y)/4, fm = x*x/(1-y)/4, and + * A = 1 (inexactly). + */ + *rx = x / npy_sqrt@c@((1 - y) * (1 + y)); + } else { /* if (y > 1) */ + /* + * A-1 = y-1 (inexactly). + */ + *rx = npy_log1p@c@((y - 1) + npy_sqrt@c@((y - 1) * (y + 1))); + } + } else { + *rx = npy_log@c@(A + npy_sqrt@c@(A * A - 1)); + } + + *new_y = y; - if (npy_fabs(x) > 1e-3 || npy_fabs(y) > 1e-3) { - /* casin(z) = -i * log( i*z + sqrt(1-z**2)) */ - @ctype@ m1, iz, r; - m1 = npy_csqrt@c@(csub@c@(c_1@c@, cmul@c@(z,z))); - iz = cmuli@c@(z); - r = npy_clog@c@(cadd@c@(iz, m1)); - r = cmuli@c@(r); - return cneg@c@(r); + if (y < FOUR_SQRT_MIN) { + /* + * Avoid a possible underflow caused by y/A. For casinh this + * would be legitimate, but will be picked up by invoking atan2 + * later on. For cacos this would not be legitimate. + */ + *B_is_usable = 0; + *sqrt_A2my2 = A * (2 / @TEPS@); + *new_y = y * (2 / @TEPS@); + return; } - else { + + /* B = (|z+I| - |z-I|) / 2 = y/A */ + *B = y / A; + *B_is_usable = 1; + + if (*B > B_crossover) { + *B_is_usable = 0; /* - * Small arguments: series expansion, to avoid loss of precision - * asin(x) = x [1 + (1/6) x^2 [1 + (9/20) x^2 [1 + ...]]] - * - * |x| < 1e-3 => |rel. error| < 1e-18 (f), 1e-24, 1e-36 (l) + * Amy = fp + fm, where fp = f(x, y+1), and fm = f(x, y-1). + * sqrt_A2my2 = sqrt(Amy*(A+y)) */ - @ctype@ z2, r; - z2 = cmul@c@(z, z); - r = c_1@c@; -#if @precision@ >= 3 - SERIES_HORNER_TERM@C@(r, z2, 81.0@C@/110); - SERIES_HORNER_TERM@C@(r, z2, 49.0@C@/72); + if (y == 1 && x < @TEPS@ / 128) { + /* + * fp is of order x^2, and fm = x/2. + * A = 1 (inexactly). + */ + *sqrt_A2my2 = npy_sqrt@c@(x) * npy_sqrt@c@((A + y) / 2); + } else if (x >= @TEPS@ * npy_fabs@c@(y - 1)) { + /* + * Underflow will not occur because + * x >= DBL_EPSILON/128 >= FOUR_SQRT_MIN + * and + * x >= DBL_EPSILON^2 >= FOUR_SQRT_MIN + */ + Amy = _f@c@(x, y + 1, R) + _f@c@(x, y - 1, S); + *sqrt_A2my2 = npy_sqrt@c@(Amy * (A + y)); + } else if (y > 1) { + /* + * fp = x*x/(y+1)/4, fm = x*x/(y-1)/4, and + * A = y (inexactly). + * + * y < RECIP_EPSILON. So the following + * scaling should avoid any underflow problems. + */ + *sqrt_A2my2 = x * (4 / @TEPS@ / @TEPS@) * y / + npy_sqrt@c@((y + 1) * (y - 1)); + *new_y = y * (4 / @TEPS@ / @TEPS@); + } else { /* if (y < 1) */ + /* + * fm = 1-y >= DBL_EPSILON, fp is of order x^2, and + * A = 1 (inexactly). + */ + *sqrt_A2my2 = npy_sqrt@c@((1 - y) * (1 + y)); + } + } +} + +/* + * Optimized version of clog() for |z| finite and larger than ~RECIP_EPSILON. + */ +static inline void _clog_for_large_values@c@(@type@ x, @type@ y, @type@ *rr, @type@ *ri) +{ +#if @precision@ == 1 + const npy_float QUARTER_SQRT_MAX = 0x1p61f; + const npy_float SQRT_MIN = 0x1p-63f; +#endif +#if @precision@ == 2 + const npy_double QUARTER_SQRT_MAX = 0x1p509; + const npy_double SQRT_MIN = 0x1p-511; #endif -#if @precision@ >= 2 - SERIES_HORNER_TERM@C@(r, z2, 25.0@C@/42); +#if @precision@ == 3 + const npy_longdouble QUARTER_SQRT_MAX = 0x1p8189l; + const npy_longdouble SQRT_MIN = 0x1p-8191l; #endif - SERIES_HORNER_TERM@C@(r, z2, 9.0@C@/20); - SERIES_HORNER_TERM@C@(r, z2, 1.0@C@/6); - return cmul@c@(r, z); + @type@ ax, ay, t; + + ax = npy_fabs@c@(x); + ay = npy_fabs@c@(y); + if (ax < ay) { + t = ax; + ax = ay; + ay = t; + } + + /* + * Avoid overflow in hypot() when x and y are both very large. + * Divide x and y by E, and then add 1 to the logarithm. This depends + * on E being larger than sqrt(2). + * Dividing by E causes an insignificant loss of accuracy; however + * this method is still poor since it is uneccessarily slow. + */ + if (ax > @TMAX@ / 2) { + *rr = npy_log@c@(npy_hypot@c@(x / NPY_E@c@, y / NPY_E@c@)) + 1; } + /* + * Avoid overflow when x or y is large. Avoid underflow when x or + * y is small. + */ + else if (ax > QUARTER_SQRT_MAX || ay < SQRT_MIN) { + *rr = npy_log@c@(npy_hypot@c@(x, y)); + } + else { + *rr = npy_log@c@(ax * ax + ay * ay) / 2; + } + *ri = npy_atan2@c@(y, x); +} +#endif + +#ifndef HAVE_CACOS@C@ +@ctype@ npy_cacos@c@(@ctype@ z) +{ +#if @precision@ == 1 + /* this is sqrt(6*EPS) */ + const npy_float SQRT_6_EPSILON = 8.4572793338e-4f; + /* chosen such that pio2_hi + pio2_lo == pio2_hi but causes FE_INEXACT. */ + const volatile npy_float pio2_lo = 7.5497899549e-9f; +#endif +#if @precision@ == 2 + const npy_double SQRT_6_EPSILON = 3.65002414998885671e-08; + const volatile npy_double pio2_lo = 6.1232339957367659e-17; +#endif +#if @precision@ == 3 + const npy_longdouble SQRT_6_EPSILON = 8.0654900873493277169e-10l; + const volatile npy_longdouble pio2_lo = 2.710505431213761085e-20l; +#endif + const @type@ RECIP_EPSILON = 1.0@c@ / @TEPS@; + const @type@ pio2_hi = NPY_PI_2@c@; + @type@ x, y, ax, ay, wx, wy, rx, ry, B, sqrt_A2mx2, new_x; + npy_int sx, sy; + npy_int B_is_usable; + + x = npy_creal@c@(z); + y = npy_cimag@c@(z); + sx = npy_signbit(x); + sy = npy_signbit(y); + ax = npy_fabs@c@(x); + ay = npy_fabs@c@(y); + + if (npy_isnan(x) || npy_isnan(y)) { + /* cacos(+-Inf + I*NaN) = NaN + I*opt(-)Inf */ + if (npy_isinf(x)) + return npy_cpack@c@(y + y, -INFINITY); + /* cacos(NaN + I*+-Inf) = NaN + I*-+Inf */ + if (npy_isinf(y)) + return npy_cpack@c@(x + x, -y); + /* cacos(0 + I*NaN) = PI/2 + I*NaN with inexact */ + if (x == 0) + return npy_cpack@c@(pio2_hi + pio2_lo, y + y); + /* + * All other cases involving NaN return NaN + I*NaN. + * C99 leaves it optional whether to raise invalid if one of + * the arguments is not NaN, so we opt not to raise it. + */ + return npy_cpack@c@(x + 0.0L + (y + 0), x + 0.0L + (y + 0)); + } + + if (ax > RECIP_EPSILON || ay > RECIP_EPSILON) { + /* clog...() will raise inexact unless x or y is infinite. */ + _clog_for_large_values@c@(x, y, &wx, &wy); + rx = npy_fabs@c@(wy); + ry = wx + NPY_LOGE2@c@; + if (sy == 0) + ry = -ry; + return npy_cpack@c@(rx, ry); + } + + /* Avoid spuriously raising inexact for z = 1. */ + if (x == 1 && y == 0) + return npy_cpack@c@(0, -y); + + /* All remaining cases are inexact. */ + raise_inexact(); + + if (ax < SQRT_6_EPSILON / 4 && ay < SQRT_6_EPSILON / 4) + return npy_cpack@c@(pio2_hi - (x - pio2_lo), -y); + + _do_hard_work@c@(ay, ax, &ry, &B_is_usable, &B, &sqrt_A2mx2, &new_x); + if (B_is_usable) { + if (sx == 0) + rx = npy_acos@c@(B); + else + rx = npy_acos@c@(-B); + } else { + if (sx == 0) + rx = npy_atan2@c@(sqrt_A2mx2, new_x); + else + rx = npy_atan2@c@(sqrt_A2mx2, -new_x); + } + if (sy == 0) + ry = -ry; + return npy_cpack@c@(rx, ry); +} +#endif + +#ifndef HAVE_CASIN@C@ +@ctype@ npy_casin@c@(@ctype@ z) +{ + /* casin(z) = I * conj( casinh(I * conj(z)) ) */ + z = npy_casinh@c@(npy_cpack@c@(npy_cimag@c@(z), npy_creal@c@(z))); + return npy_cpack@c@(npy_cimag@c@(z), npy_creal@c@(z)); } #endif @@ -932,49 +1213,112 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) #ifndef HAVE_CACOSH@C@ @ctype@ npy_cacosh@c@(@ctype@ z) { - /* cacosh(z) = log( z + sqrt(z+1)*sqrt(z-1)) */ - @ctype@ p1, m1; - p1 = npy_csqrt@c@(cadd@c@(z, c_1@c@)); - m1 = npy_csqrt@c@(csub@c@(z, c_1@c@)); - return npy_clog@c@(cadd@c@(z, cmul@c@(p1,m1))); + /* + * cacosh(z) = I*cacos(z) or -I*cacos(z) + * where the sign is chosen so Re(cacosh(z)) >= 0. + */ + @ctype@ w; + @type@ rx, ry; + + w = npy_cacos@c@(z); + rx = npy_creal@c@(w); + ry = npy_cimag@c@(w); + /* cacosh(NaN + I*NaN) = NaN + I*NaN */ + if (npy_isnan(rx) && npy_isnan(ry)) + return npy_cpack@c@(ry, rx); + /* cacosh(NaN + I*+-Inf) = +Inf + I*NaN */ + /* cacosh(+-Inf + I*NaN) = +Inf + I*NaN */ + if (npy_isnan(rx)) + return npy_cpack@c@(npy_fabs@c@(ry), rx); + /* cacosh(0 + I*NaN) = NaN + I*NaN */ + if (npy_isnan(ry)) + return npy_cpack@c@(ry, ry); + return npy_cpack@c@(npy_fabs@c@(ry), npy_copysign@c@(rx, npy_cimag@c@(z))); } #endif #ifndef HAVE_CASINH@C@ +/* + * casinh(z) = z + O(z^3) as z -> 0 + * + * casinh(z) = sign(x)*clog(sign(x)*z) + O(1/z^2) as z -> infinity + * The above formula works for the imaginary part as well, because + * Im(casinh(z)) = sign(x)*atan2(sign(x)*y, fabs(x)) + O(y/z^3) + * as z -> infinity, uniformly in y + */ @ctype@ npy_casinh@c@(@ctype@ z) { - @type@ x, y; +#if @precision@ == 1 + /* this is sqrt(6*EPS) */ + const npy_float SQRT_6_EPSILON = 8.4572793338e-4f; + /* chosen such that pio2_hi + pio2_lo == pio2_hi but causes FE_INEXACT. */ + const volatile npy_float pio2_lo = 7.5497899549e-9f; +#endif +#if @precision@ == 2 + const npy_double SQRT_6_EPSILON = 3.65002414998885671e-08; + const volatile npy_double pio2_lo = 6.1232339957367659e-17; +#endif +#if @precision@ == 3 + const npy_longdouble SQRT_6_EPSILON = 8.0654900873493277169e-10l; + const volatile npy_longdouble pio2_lo = 2.710505431213761085e-20l; +#endif + const @type@ RECIP_EPSILON = 1.0@c@ / @TEPS@; + const @type@ pio2_hi = NPY_PI_2@c@; + @type@ x, y, ax, ay, wx, wy, rx, ry, B, sqrt_A2my2, new_y; + npy_int B_is_usable; x = npy_creal@c@(z); y = npy_cimag@c@(z); + ax = npy_fabs@c@(x); + ay = npy_fabs@c@(y); - if (npy_fabs(x) > 1e-3 || npy_fabs(y) > 1e-3) { - /* casinh(z) = log( z + sqrt(1 + z**2)) */ - @ctype@ p1; - p1 = npy_csqrt@c@(cadd@c@(c_1@c@, cmul@c@(z, z))); - return npy_clog@c@(cadd@c@(p1, z)); - } - else { + if (npy_isnan(x) || npy_isnan(y)) { + /* casinh(+-Inf + I*NaN) = +-Inf + I*NaN */ + if (npy_isinf(x)) + return npy_cpack@c@(x, y + y); + /* casinh(NaN + I*+-Inf) = opt(+-)Inf + I*NaN */ + if (npy_isinf(y)) + return npy_cpack@c@(y, x + x); + /* casinh(NaN + I*0) = NaN + I*0 */ + if (y == 0) + return npy_cpack@c@(x + x, y); /* - * Small arguments: series expansion, to avoid loss of precision - * asinh(x) = x [1 - (1/6) x^2 [1 - (9/20) x^2 [1 - ...]]] - * - * |x| < 1e-3 => |rel. error| < 1e-18 (f), 1e-24, 1e-36 (l) + * All other cases involving NaN return NaN + I*NaN. + * C99 leaves it optional whether to raise invalid if one of + * the arguments is not NaN, so we opt not to raise it. */ - @ctype@ z2, r; - z2 = cmul@c@(z, z); - r = c_1@c@; -#if @precision@ >= 3 - SERIES_HORNER_TERM@C@(r, z2, -81.0@C@/110); - SERIES_HORNER_TERM@C@(r, z2, -49.0@C@/72); -#endif -#if @precision@ >= 2 - SERIES_HORNER_TERM@C@(r, z2, -25.0@C@/42); -#endif - SERIES_HORNER_TERM@C@(r, z2, -9.0@C@/20); - SERIES_HORNER_TERM@C@(r, z2, -1.0@C@/6); - return cmul@c@(z, r); + return npy_cpack@c@(x + 0.0L + (y + 0), x + 0.0L + (y + 0)); + } + + if (ax > RECIP_EPSILON || ay > RECIP_EPSILON) { + /* clog...() will raise inexact unless x or y is infinite. */ + if (npy_signbit(x) == 0) { + _clog_for_large_values@c@(x, y, &wx, &wy); + wx += NPY_LOGE2@c@; + } + else { + _clog_for_large_values@c@(-x, -y, &wx, &wy); + wx += NPY_LOGE2@c@; + } + return npy_cpack@c@(npy_copysign@c@(wx, x), npy_copysign@c@(wy, y)); } + + /* Avoid spuriously raising inexact for z = 0. */ + if (x == 0 && y == 0) + return (z); + + /* All remaining cases are inexact. */ + raise_inexact(); + + if (ax < SQRT_6_EPSILON / 4 && ay < SQRT_6_EPSILON / 4) + return (z); + + _do_hard_work@c@(ax, ay, &rx, &B_is_usable, &B, &sqrt_A2my2, &new_y); + if (B_is_usable) + ry = npy_asin@c@(B); + else + ry = npy_atan2@c@(new_y, sqrt_A2my2); + return npy_cpack@c@(npy_copysign@c@(rx, x), npy_copysign@c@(ry, y)); } #endif @@ -1110,19 +1454,18 @@ static inline npy_longdouble _real_part_reciprocall(npy_longdouble x, npy_longdo /* this is sqrt(3*EPS) */ const npy_float SQRT_3_EPSILON = 5.9801995673e-4f; /* chosen such that pio2_hi + pio2_lo == pio2_hi but causes FE_INEXACT. */ - const volatile float pio2_lo = 7.5497899549e-9f; + const volatile npy_float pio2_lo = 7.5497899549e-9f; #endif #if @precision@ == 2 const npy_double SQRT_3_EPSILON = 2.5809568279517849e-8; const volatile npy_double pio2_lo = 6.1232339957367659e-17; #endif #if @precision@ == 3 - const npy_longdouble SQRT_3_EPSILON = 5.70316273435758915310e-10; + const npy_longdouble SQRT_3_EPSILON = 5.70316273435758915310e-10l; const volatile npy_longdouble pio2_lo = 2.710505431213761085e-20l; #endif const @type@ RECIP_EPSILON = 1.0@c@ / @TEPS@; const @type@ pio2_hi = NPY_PI_2@c@; - const volatile float tiny = 3.9443045e-31f; @type@ x, y, ax, ay, rx, ry; x = npy_creal@c@(z); @@ -1164,7 +1507,7 @@ static inline npy_longdouble _real_part_reciprocall(npy_longdouble x, npy_longdo * inexact, but this is the only only that needs to do it * explicitly. */ - volatile npy_float junk = 1 + tiny; + raise_inexact(); return (z); } diff --git a/numpy/core/test_c99complex.c b/numpy/core/test_c99complex.c index 2c1e0047c4bc..e3abffffe9cb 100644 --- a/numpy/core/test_c99complex.c +++ b/numpy/core/test_c99complex.c @@ -11,8 +11,10 @@ #define TYPE float #define SUFFIX f #define EPS FLT_EPSILON -#define CLOSE_ATOL 0 -#define CLOSE_RTOL 1e-5 +#define CLOSE_ATOL 0.0f +#define CLOSE_RTOL 1e-5f +#define BRANCH_SCALE 1e2f +#define BRANCH_ATOL 1e-2f #define FMT "%.8e" #define NPY_PI_2 1.570796326794896619231321691639751442f #define NPY_PI 3.141592653589793238462643383279502884f @@ -23,8 +25,10 @@ #define TYPE double #define SUFFIX #define EPS DBL_EPSILON -#define CLOSE_ATOL 0 +#define CLOSE_ATOL 0.0 #define CLOSE_RTOL 1e-12 +#define BRANCH_SCALE 1e3 +#define BRANCH_ATOL 1e-4 #define FMT "%.16e" #define NPY_PI_2 1.570796326794896619231321691639751442 #define NPY_PI 3.141592653589793238462643383279502884 @@ -34,9 +38,11 @@ #ifdef LONGDOUBLE #define TYPE long double #define SUFFIX l -#define EPS 50*LDBL_EPSILON -#define CLOSE_ATOL 0 -#define CLOSE_RTOL 1e-12 +#define EPS LDBL_EPSILON +#define CLOSE_ATOL 0.0l +#define CLOSE_RTOL 1e-12l +#define BRANCH_SCALE 1e3l +#define BRANCH_ATOL 1e-4l #define FMT "%.18Le" #define NPY_PI_2 1.570796326794896619231321691639751442L #define NPY_PI 3.141592653589793238462643383279502884L @@ -248,9 +254,9 @@ const TYPE NZERO = -1.0 * 0.0; if (!q) { \ ret = 0; \ printf(STRINGIZE(func) STRINGIZE(SUFFIX) ": branch cut failure: " \ - "x = " FMT " + " FMT "j, dx = " FMT " + " FMT "j, rsign = %d, " \ - "isign = %d, check_sign_zero = %d\n", vxr, vxi, \ - vdxr, vdxi, vrsign, visign, vcksignzero); \ + "x = " FMT " + " FMT "j, dx = " FMT " + " FMT \ + "j, rsign = %d, isign = %d, check_sign_zero = %d\n", vxr, \ + vxi, vdxr, vdxi, vrsign, visign, vcksignzero); \ } \ } \ while(0) @@ -316,8 +322,8 @@ typedef TYPE (*realfunc)(TYPE); int check_branch_cut(complexfunc cfunc, TYPE complex x0, TYPE complex dx, int re_sign, int im_sign, int sig_zero_ok) { - const TYPE scale = EPS * 1e2; - const TYPE atol = 1e-4; + const TYPE scale = EPS * BRANCH_SCALE; + const TYPE atol = BRANCH_ATOL; TYPE complex shift = dx*scale*ADDSUFFIX(cabs)(x0)/ADDSUFFIX(cabs)(dx); TYPE complex y0 = cfunc(x0); @@ -502,7 +508,7 @@ int test_cacos() TEST_CE(cacos, -INFINITY, -INFINITY, 0.75 * NPY_PI, INFINITY); TEST_CE(cacos, INFINITY, INFINITY, 0.25 * NPY_PI, -INFINITY); - TEST_CE(cacos, INFINITY, -INFINITY, 0.25 * NPY_PI, -INFINITY); + TEST_CE(cacos, INFINITY, -INFINITY, 0.25 * NPY_PI, INFINITY); /* sign of imaginary part is unspecified. */ TEST_UNSPECIFIED2(cacos, INFINITY, NAN, NAN, INFINITY, NAN, -INFINITY); @@ -518,7 +524,7 @@ int test_cacos() TEST_EE(cacos, NAN, NAN, NAN, NAN); TEST_BRANCH_CUT(cacos, -2, 0, 0, 1, 1, -1, 1); - TEST_BRANCH_CUT(cacos, 2, 0, 0, -1, 1, -1, 1); + TEST_BRANCH_CUT(cacos, 2, 0, 0, 1, 1, -1, 1); TEST_BRANCH_CUT(cacos, 0, -2, 1, 0, 1, 1, 1); TEST_BRANCH_CUT(cacos, 0, 2, 1, 0, 1, 1, 1); @@ -579,7 +585,7 @@ int test_casin() TEST_CC(casin, 1e-5, 1e-5, 9.999999999666666667e-6, 1.0000000000333333333e-5); TEST_BRANCH_CUT(casin, -2, 0, 0, 1, 1, -1, 1); - TEST_BRANCH_CUT(casin, 2, 0, 0, -1, 1, -1, 1); + TEST_BRANCH_CUT(casin, 2, 0, 0, 1, 1, -1, 1); TEST_BRANCH_CUT(casin, 0, -2, 1, 0, 1, 1, 1); TEST_BRANCH_CUT(casin, 0, 2, 1, 0, 1, 1, 1); @@ -755,7 +761,7 @@ int test_casinh() TEST_CC(casinh, 1e-5, 1e-5, 1.0000000000333333333e-5, 9.999999999666666667e-6); - TEST_BRANCH_CUT(casinh, 0, -2, -1, 0, -1, 1, 1); + TEST_BRANCH_CUT(casinh, 0, -2, 1, 0, -1, 1, 1); TEST_BRANCH_CUT(casinh, 0, 2, 1, 0, -1, 1, 1); TEST_BRANCH_CUT(casinh, -2, 0, 0, 1, 1, 1, 1); TEST_BRANCH_CUT(casinh, 2, 0, 0, 1, 1, 1, 1); diff --git a/numpy/core/tests/test_umath.py b/numpy/core/tests/test_umath.py index f747aa89c290..8a01cd54ef83 100644 --- a/numpy/core/tests/test_umath.py +++ b/numpy/core/tests/test_umath.py @@ -1120,11 +1120,11 @@ def test_branch_cuts(self): yield _check_branch_cut, np.log1p, -1.5, 1j, 1, -1 yield _check_branch_cut, np.sqrt, -0.5, 1j, 1, -1 - yield _check_branch_cut, np.arcsin, [ -2, 2], [1j, 0-1j], 1, -1 - yield _check_branch_cut, np.arccos, [ -2, 2], [1j, 0-1j], 1, -1 + yield _check_branch_cut, np.arcsin, [ -2, 2], [1j, 1j], 1, -1 + yield _check_branch_cut, np.arccos, [ -2, 2], [1j, 1j], 1, -1 yield _check_branch_cut, np.arctan, [0-2j, 2j], [1, 1 ], -1, 1 - yield _check_branch_cut, np.arcsinh, [0-2j, 2j], [-1, 1], -1, 1 + yield _check_branch_cut, np.arcsinh, [0-2j, 2j], [1, 1], -1, 1 yield _check_branch_cut, np.arccosh, [ -1, 0.5], [1j, 1j], 1, -1 yield _check_branch_cut, np.arctanh, [ -2, 2], [1j, 1j], 1, -1 @@ -1147,10 +1147,10 @@ def test_branch_cuts_failing(self): yield _check_branch_cut, np.log1p, -1.5, 1j, 1, -1, True # XXX: signed zeros are not OK for sqrt or for the arc* functions yield _check_branch_cut, np.sqrt, -0.5, 1j, 1, -1, True - yield _check_branch_cut, np.arcsin, [ -2, 2], [1j, -1j], 1, -1, True - yield _check_branch_cut, np.arccos, [ -2, 2], [1j, -1j], 1, -1, True + yield _check_branch_cut, np.arcsin, [ -2, 2], [1j, 1j], 1, -1, True + yield _check_branch_cut, np.arccos, [ -2, 2], [1j, 1j], 1, -1, True yield _check_branch_cut, np.arctan, [0-2j, 2j], [1, 1 ], -1, 1, True - yield _check_branch_cut, np.arcsinh, [0-2j, 2j], [-1, 1], -1, 1, True + yield _check_branch_cut, np.arcsinh, [0-2j, 2j], [1, 1], -1, 1, True yield _check_branch_cut, np.arccosh, [ -1, 0.5], [1j, 1j], 1, -1, True yield _check_branch_cut, np.arctanh, [ -2, 2], [1j, 1j], 1, -1, True From c8f13ee31eedd33fb6286ecfaecb19fe3013b888 Mon Sep 17 00:00:00 2001 From: Eric Moore Date: Thu, 10 Oct 2013 12:06:46 -0400 Subject: [PATCH 22/35] TST: Enable signed zero branch cut tests Now we have our own implementations that can pass these tests and we only link against the system versions if they can also pass. Additionally, run the branch cut tests for complex64. --- numpy/core/tests/test_umath.py | 94 ++++++++++++++++++++-------------- 1 file changed, 55 insertions(+), 39 deletions(-) diff --git a/numpy/core/tests/test_umath.py b/numpy/core/tests/test_umath.py index 8a01cd54ef83..3f31902fb296 100644 --- a/numpy/core/tests/test_umath.py +++ b/numpy/core/tests/test_umath.py @@ -1114,19 +1114,19 @@ def test_precisions_consistent(self) : def test_branch_cuts(self): # check branch cuts and continuity on them - yield _check_branch_cut, np.log, -0.5, 1j, 1, -1 - yield _check_branch_cut, np.log2, -0.5, 1j, 1, -1 - yield _check_branch_cut, np.log10, -0.5, 1j, 1, -1 - yield _check_branch_cut, np.log1p, -1.5, 1j, 1, -1 - yield _check_branch_cut, np.sqrt, -0.5, 1j, 1, -1 + yield _check_branch_cut, np.log, -0.5, 1j, 1, -1, True + yield _check_branch_cut, np.log2, -0.5, 1j, 1, -1, True + yield _check_branch_cut, np.log10, -0.5, 1j, 1, -1, True + yield _check_branch_cut, np.log1p, -1.5, 1j, 1, -1, True + yield _check_branch_cut, np.sqrt, -0.5, 1j, 1, -1, True - yield _check_branch_cut, np.arcsin, [ -2, 2], [1j, 1j], 1, -1 - yield _check_branch_cut, np.arccos, [ -2, 2], [1j, 1j], 1, -1 - yield _check_branch_cut, np.arctan, [0-2j, 2j], [1, 1 ], -1, 1 + yield _check_branch_cut, np.arcsin, [ -2, 2], [1j, 1j], 1, -1, True + yield _check_branch_cut, np.arccos, [ -2, 2], [1j, 1j], 1, -1, True + yield _check_branch_cut, np.arctan, [0-2j, 2j], [1, 1 ], -1, 1, True - yield _check_branch_cut, np.arcsinh, [0-2j, 2j], [1, 1], -1, 1 - yield _check_branch_cut, np.arccosh, [ -1, 0.5], [1j, 1j], 1, -1 - yield _check_branch_cut, np.arctanh, [ -2, 2], [1j, 1j], 1, -1 + yield _check_branch_cut, np.arcsinh, [0-2j, 2j], [1, 1], -1, 1, True + yield _check_branch_cut, np.arccosh, [ -1, 0.5], [1j, 1j], 1, -1, True + yield _check_branch_cut, np.arctanh, [ -2, 2], [1j, 1j], 1, -1, True # check against bogus branch cuts: assert continuity between quadrants yield _check_branch_cut, np.arcsin, [0-2j, 2j], [ 1, 1], 1, 1 @@ -1137,22 +1137,30 @@ def test_branch_cuts(self): yield _check_branch_cut, np.arccosh, [0-2j, 2j, 2], [1, 1, 1j], 1, 1 yield _check_branch_cut, np.arctanh, [0-2j, 2j, 0], [1, 1, 1j], 1, 1 - @dec.knownfailureif(True, "These branch cuts are known to fail") - def test_branch_cuts_failing(self): - # XXX: signed zero not OK with ICC on 64-bit platform for log, see - # http://permalink.gmane.org/gmane.comp.python.numeric.general/25335 - yield _check_branch_cut, np.log, -0.5, 1j, 1, -1, True - yield _check_branch_cut, np.log2, -0.5, 1j, 1, -1, True - yield _check_branch_cut, np.log10, -0.5, 1j, 1, -1, True - yield _check_branch_cut, np.log1p, -1.5, 1j, 1, -1, True - # XXX: signed zeros are not OK for sqrt or for the arc* functions - yield _check_branch_cut, np.sqrt, -0.5, 1j, 1, -1, True - yield _check_branch_cut, np.arcsin, [ -2, 2], [1j, 1j], 1, -1, True - yield _check_branch_cut, np.arccos, [ -2, 2], [1j, 1j], 1, -1, True - yield _check_branch_cut, np.arctan, [0-2j, 2j], [1, 1 ], -1, 1, True - yield _check_branch_cut, np.arcsinh, [0-2j, 2j], [1, 1], -1, 1, True - yield _check_branch_cut, np.arccosh, [ -1, 0.5], [1j, 1j], 1, -1, True - yield _check_branch_cut, np.arctanh, [ -2, 2], [1j, 1j], 1, -1, True + def test_branch_cuts_complex64(self): + # check branch cuts and continuity on them + yield _check_branch_cut, np.log, -0.5, 1j, 1, -1, True, np.complex64 + yield _check_branch_cut, np.log2, -0.5, 1j, 1, -1, True, np.complex64 + yield _check_branch_cut, np.log10, -0.5, 1j, 1, -1, True, np.complex64 + yield _check_branch_cut, np.log1p, -1.5, 1j, 1, -1, True, np.complex64 + yield _check_branch_cut, np.sqrt, -0.5, 1j, 1, -1, True, np.complex64 + + yield _check_branch_cut, np.arcsin, [ -2, 2], [1j, 1j], 1, -1, True, np.complex64 + yield _check_branch_cut, np.arccos, [ -2, 2], [1j, 1j], 1, -1, True, np.complex64 + yield _check_branch_cut, np.arctan, [0-2j, 2j], [1, 1 ], -1, 1, True, np.complex64 + + yield _check_branch_cut, np.arcsinh, [0-2j, 2j], [1, 1], -1, 1, True, np.complex64 + yield _check_branch_cut, np.arccosh, [ -1, 0.5], [1j, 1j], 1, -1, True, np.complex64 + yield _check_branch_cut, np.arctanh, [ -2, 2], [1j, 1j], 1, -1, True, np.complex64 + + # check against bogus branch cuts: assert continuity between quadrants + yield _check_branch_cut, np.arcsin, [0-2j, 2j], [ 1, 1], 1, 1, False, np.complex64 + yield _check_branch_cut, np.arccos, [0-2j, 2j], [ 1, 1], 1, 1, False, np.complex64 + yield _check_branch_cut, np.arctan, [ -2, 2], [1j, 1j], 1, 1, False, np.complex64 + + yield _check_branch_cut, np.arcsinh, [ -2, 2, 0], [1j, 1j, 1 ], 1, 1, False, np.complex64 + yield _check_branch_cut, np.arccosh, [0-2j, 2j, 2], [1, 1, 1j], 1, 1, False, np.complex64 + yield _check_branch_cut, np.arctanh, [0-2j, 2j, 0], [1, 1, 1j], 1, 1, False, np.complex64 def test_against_cmath(self): import cmath, sys @@ -1316,8 +1324,12 @@ def _check_branch_cut(f, x0, dx, re_sign=1, im_sign=-1, sig_zero_ok=False, x0 = np.atleast_1d(x0).astype(dtype) dx = np.atleast_1d(dx).astype(dtype) - scale = np.finfo(dtype).eps * 1e3 - atol = 1e-4 + if np.dtype(dtype).char == 'F': + scale = np.finfo(dtype).eps * 1e2 + atol = np.float32(1e-2) + else: + scale = np.finfo(dtype).eps * 1e3 + atol = 1e-4 y0 = f(x0) yp = f(x0 + dx*scale*np.absolute(x0)/np.absolute(dx)) @@ -1332,16 +1344,20 @@ def _check_branch_cut(f, x0, dx, re_sign=1, im_sign=-1, sig_zero_ok=False, # check that signed zeros also work as a displacement jr = (x0.real == 0) & (dx.real != 0) ji = (x0.imag == 0) & (dx.imag != 0) - - x = -x0 - x.real[jr] = 0.*dx.real - x.imag[ji] = 0.*dx.imag - x = -x - ym = f(x) - ym = ym[jr | ji] - y0 = y0[jr | ji] - assert_(np.all(np.absolute(y0.real - ym.real*re_sign) < atol), (y0, ym)) - assert_(np.all(np.absolute(y0.imag - ym.imag*im_sign) < atol), (y0, ym)) + if np.any(jr): + x = x0[jr] + x.real = np.NZERO + ym = f(x) + assert_(np.all(np.absolute(y0[jr].real - ym.real*re_sign) < atol), (y0[jr], ym)) + assert_(np.all(np.absolute(y0[jr].imag - ym.imag*im_sign) < atol), (y0[jr], ym)) + + + if np.any(ji): + x = x0[ji] + x.imag = np.NZERO + ym = f(x) + assert_(np.all(np.absolute(y0[ji].real - ym.real*re_sign) < atol), (y0[ji], ym)) + assert_(np.all(np.absolute(y0[ji].imag - ym.imag*im_sign) < atol), (y0[ji], ym)) def test_copysign(): assert_(np.copysign(1, -1) == -1) From eab5228ac6d3aeae7f915f3c2a9f12f50f50dfdc Mon Sep 17 00:00:00 2001 From: Eric Moore Date: Thu, 10 Oct 2013 13:28:21 -0400 Subject: [PATCH 23/35] BUG: fix cpow tests to match what npy_cpow does. Annex G of the C99 standard does not define any special values for cpow, allowing the implementation to be as simple as cpow(z,w) = cexp(w*clog(z)). We have a large number of tests for our cpow function, both in the test suite and in test_c99complex.c. (There are actually more in test_c99complex.c, since I'm doing all combinations from TestCpow::test_scalar in test_umath_complex.py.) As of right now, these tests probably mean that we will never use a system cpow implemenation. This is fine, since we handle a large number of edge cases in a sane way and at least glibc does not. With this commit all 48 of our complex functions pass test_c99complex.c. --- numpy/core/test_c99complex.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/numpy/core/test_c99complex.c b/numpy/core/test_c99complex.c index e3abffffe9cb..bb841aabbd72 100644 --- a/numpy/core/test_c99complex.c +++ b/numpy/core/test_c99complex.c @@ -1458,7 +1458,7 @@ int test_cpow() TEST_CPOW_CC(0, 1, 1, 0, 0, 1); TEST_CPOW_CC(0, 1, 0, 1, ADDSUFFIX(exp)(-NPY_PI_2), 0); - TEST_CPOW_CC(0, 1, -0.5, 1.5, 0.067019739708273365, 0.067019739708273365); + TEST_CPOW_CC(0, 1, -0.5, 1.5, 0.067019739708273365, -0.067019739708273365); TEST_CPOW_CC(0, 1, 2, 0, -1, 0); TEST_CPOW_CC(0, 1, 3, 0, 0, -1); @@ -1473,13 +1473,13 @@ int test_cpow() TEST_CPOW_CC(2.5, 0.375, 2, 0, 391.0/64.0, 15.0/8.0); TEST_CPOW_CC(2.5, 0.375, 3, 0, 1865.0/128.0, 3573.0/512.0); - TEST_CPOW_EE(INFINITY, 0, 1, 0, NAN, NAN); + TEST_CPOW_EE(INFINITY, 0, 1, 0, INFINITY, 0); TEST_CPOW_EE(INFINITY, 0, 0, 1, NAN, NAN); - TEST_CPOW_EE(INFINITY, 0, -0.5, 1.5, NAN, NAN); - TEST_CPOW_EE(INFINITY, 0, 2, 0, NAN, NAN); + TEST_CPOW_EE(INFINITY, 0, -0.5, 1.5, 0, 0); + TEST_CPOW_EE(INFINITY, 0, 2, 0, INFINITY, NAN); TEST_CPOW_EE(INFINITY, 0, 3, 0, NAN, NAN); - TEST_CPOW_EE(NAN, 0, 1, 0, NAN, NAN); + TEST_CPOW_EE(NAN, 0, 1, 0, NAN, 0); TEST_CPOW_EE(NAN, 0, 0, 1, NAN, NAN); TEST_CPOW_EE(NAN, 0, -0.5, 1.5, NAN, NAN); TEST_CPOW_EE(NAN, 0, 2, 0, NAN, NAN); From 2b5f1ada9ba1e419d41e46a27e783f1cda0a8a17 Mon Sep 17 00:00:00 2001 From: Eric Moore Date: Thu, 10 Oct 2013 13:52:12 -0400 Subject: [PATCH 24/35] STY: long lines, whitespace --- numpy/core/setup.py | 2 +- numpy/core/src/npymath/npy_math_complex.c.src | 26 ++++++++++++------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/numpy/core/setup.py b/numpy/core/setup.py index 4f462205ea18..aab7d0b0ca68 100644 --- a/numpy/core/setup.py +++ b/numpy/core/setup.py @@ -231,7 +231,7 @@ def check_prec(prec): exists.append(f) else: exists.extend(C99_COMPLEX_FUNCS_CHECKED) - + if len(exists) > 0: fp = open('./numpy/core/test_c99complex.c', 'r') obody = fp.read() diff --git a/numpy/core/src/npymath/npy_math_complex.c.src b/numpy/core/src/npymath/npy_math_complex.c.src index 7c6d2f499ddf..aa404ca12d9d 100644 --- a/numpy/core/src/npymath/npy_math_complex.c.src +++ b/numpy/core/src/npymath/npy_math_complex.c.src @@ -536,17 +536,20 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) return npy_cpack@c@(npy_cosh@c@(x), x * y); absx = npy_fabs@c@(x); if (absx < CCOSH_BIG) /* small x: normal case */ - return npy_cpack@c@(npy_cosh@c@(x) * npy_cos@c@(y), npy_sinh@c@(x) * npy_sin@c@(y)); + return npy_cpack@c@(npy_cosh@c@(x) * npy_cos@c@(y), + npy_sinh@c@(x) * npy_sin@c@(y)); /* |x| >= 22, so cosh(x) ~= exp(|x|) */ if (absx < SCALED_CEXP_LOWER@C@) { /* x < 710: exp(|x|) won't overflow */ h = npy_exp@c@(absx) * 0.5; - return npy_cpack@c@(h * npy_cos@c@(y), npy_copysign@c@(h, x) * npy_sin@c@(y)); + return npy_cpack@c@(h * npy_cos@c@(y), + npy_copysign@c@(h, x) * npy_sin@c@(y)); } else if (absx < SCALED_CEXP_UPPER@C@) { /* x < 1455: scale to avoid overflow */ z = _npy_scaled_cexp@c@(absx, y, -1); - return npy_cpack@c@(npy_creal@c@(z), npy_cimag@c@(z) * npy_copysign@c@(1, x)); + return npy_cpack@c@(npy_creal@c@(z), + npy_cimag@c@(z) * npy_copysign@c@(1, x)); } else { /* x >= 1455: the result always overflows */ h = CCOSH_HUGE * x; @@ -661,17 +664,20 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) return npy_cpack@c@(npy_sinh@c@(x), y); absx = npy_fabs@c@(x); if (absx < CSINH_BIG) /* small x: normal case */ - return npy_cpack@c@(npy_sinh@c@(x) * npy_cos@c@(y), npy_cosh@c@(x) * npy_sin@c@(y)); + return npy_cpack@c@(npy_sinh@c@(x) * npy_cos@c@(y), + npy_cosh@c@(x) * npy_sin@c@(y)); /* |x| >= 22, so cosh(x) ~= exp(|x|) */ if (absx < SCALED_CEXP_LOWER@C@) { /* x < 710: exp(|x|) won't overflow */ h = npy_exp@c@(npy_fabs@c@(x)) * 0.5; - return npy_cpack@c@(npy_copysign@c@(h, x) * npy_cos@c@(y), h * npy_sin@c@(y)); + return npy_cpack@c@(npy_copysign@c@(h, x) * npy_cos@c@(y), + h * npy_sin@c@(y)); } else if (x < SCALED_CEXP_UPPER@C@) { /* x < 1455: scale to avoid overflow */ z = _npy_scaled_cexp@c@(absx, y, -1); - return npy_cpack@c@(npy_creal@c@(z) * npy_copysign@c@(1, x), npy_cimag@c@(z)); + return npy_cpack@c@(npy_creal@c@(z) * npy_copysign@c@(1, x), + npy_cimag@c@(z)); } else { /* x >= 1455: the result always overflows */ h = CSINH_HUGE * x; @@ -1058,7 +1064,8 @@ static inline void _do_hard_work@c@(@type@ x, @type@ y, @type@ *rx, /* * Optimized version of clog() for |z| finite and larger than ~RECIP_EPSILON. */ -static inline void _clog_for_large_values@c@(@type@ x, @type@ y, @type@ *rr, @type@ *ri) +static inline void _clog_for_large_values@c@(@type@ x, @type@ y, + @type@ *rr, @type@ *ri) { #if @precision@ == 1 const npy_float QUARTER_SQRT_MAX = 0x1p61f; @@ -1387,7 +1394,7 @@ static inline npy_float _real_part_reciprocalf(npy_float x, npy_float y) #endif #if @precision@ == 2 #define BIAS (DBL_MAX_EXP - 1) -/* XXX more guard digits are useful iff there is extra precision. */ +/* more guard digits are useful iff there is extra precision. */ #define CUTOFF (DBL_MANT_DIG / 2 + 1) /* just half or 1 guard digit */ static inline npy_double _real_part_reciprocal(npy_double x, npy_double y) { @@ -1421,7 +1428,8 @@ static inline npy_double _real_part_reciprocal(npy_double x, npy_double y) #if @precision@ == 3 #define BIAS (LDBL_MAX_EXP - 1) #define CUTOFF (LDBL_MANT_DIG / 2 + 1) -static inline npy_longdouble _real_part_reciprocall(npy_longdouble x, npy_longdouble y) +static inline npy_longdouble _real_part_reciprocall(npy_longdouble x, + npy_longdouble y) { npy_longdouble scale; union IEEEl2bitsrep ux, uy, us; From 1fb1b74e9e3a76010f5f3f8c5e6772ed710a5b29 Mon Sep 17 00:00:00 2001 From: Eric Moore Date: Fri, 11 Oct 2013 12:30:42 -0400 Subject: [PATCH 25/35] MAINT: remove gccisms and unbreak MSVC build. --- numpy/core/src/npymath/npy_math_complex.c.src | 108 ++++++++++-------- 1 file changed, 63 insertions(+), 45 deletions(-) diff --git a/numpy/core/src/npymath/npy_math_complex.c.src b/numpy/core/src/npymath/npy_math_complex.c.src index aa404ca12d9d..68c629b24d82 100644 --- a/numpy/core/src/npymath/npy_math_complex.c.src +++ b/numpy/core/src/npymath/npy_math_complex.c.src @@ -175,7 +175,7 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) #if @precision@ == 3 const npy_int k = 19547; #endif - const @type@ kln2 = k * NPY_LOGE2; + const @type@ kln2 = k * NPY_LOGE2@c@; @type@ mant, mantcos, mantsin; npy_int ex, excos, exsin; @@ -211,7 +211,7 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) if (npy_isfinite(i)) { ret = npy_cpack@c@(x * c, x * s); } else { - ret = npy_cpack@c@(NPY_NAN, npy_copysign@c@(NPY_NAN, i)); + ret = npy_cpack@c@(NPY_NAN@C@, npy_copysign@c@(NPY_NAN@C@, i)); } } @@ -220,7 +220,7 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) if (i == 0) { ret = z; } else { - ret = npy_cpack@c@(r, npy_copysign@c@(NPY_NAN, i)); + ret = npy_cpack@c@(r, npy_copysign@c@(NPY_NAN@C@, i)); } } else { /* r is +- inf */ @@ -235,7 +235,7 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) } else { /* x = +inf, y = +-inf | nan */ npy_set_floatstatus_invalid(); - ret = npy_cpack@c@(r, NPY_NAN); + ret = npy_cpack@c@(r, NPY_NAN@C@); } } else { if (npy_isfinite(i)) { @@ -342,7 +342,7 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) if (a == 0 && b == 0) return (npy_cpack@c@(0, b)); if (npy_isinf(b)) - return (npy_cpack@c@(NPY_INFINITY, b)); + return (npy_cpack@c@(NPY_INFINITY@C@, b)); if (npy_isnan(a)) { t = (b - b) / (b - b); /* raise invalid if b is not a NaN */ return (npy_cpack@c@(a, t)); /* return NaN + NaN i */ @@ -375,10 +375,10 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) /* Algorithm 312, CACM vol 10, Oct 1967. */ if (a >= 0) { - t = npy_sqrt@c@((a + npy_hypot@c@(a, b)) * 0.5); + t = npy_sqrt@c@((a + npy_hypot@c@(a, b)) * 0.5@c@); result = npy_cpack@c@(t, b / (2 * t)); } else { - t = npy_sqrt@c@((-a + npy_hypot@c@(a, b)) * 0.5); + t = npy_sqrt@c@((-a + npy_hypot@c@(a, b)) * 0.5@c@); result = npy_cpack@c@(npy_fabs@c@(b) / (2 * t), npy_copysign@c@(t, b)); } @@ -409,17 +409,17 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) return npy_cpack@c@(0., 0.); } else { - volatile @type@ tmp = NPY_INFINITY; + volatile @type@ tmp = NPY_INFINITY@C@; /* NB: there are four complex zeros; c0 = (+-0, +-0), so that unlike * for reals, c0**p, with `p` negative is in general * ill-defined. * * c0**z with z complex is also ill-defined. */ - r = npy_cpack@c@(NPY_NAN, NPY_NAN); + r = npy_cpack@c@(NPY_NAN@C@, NPY_NAN@C@); /* Raise invalid */ - tmp -= NPY_INFINITY; + tmp -= NPY_INFINITY@C@; ar = tmp; return r; } @@ -506,21 +506,26 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) * spacing(0.5 * exp(CCOSH_BIG)) > 0.5*exp(-CCOSH_BIG) * although the exact value assigned to CCOSH_BIG is not so important */ +@ctype@ npy_ccosh@c@(@ctype@ z) +{ #if @precision@ == 1 -#define CCOSH_BIG 9.0f -#define CCOSH_HUGE 1.70141183e+38f + const npy_float CCOSH_BIG = 9.0f; + const npy_float CCOSH_HUGE = 1.70141183e+38f; #endif #if @precision@ == 2 -#define CCOSH_BIG 22.0 -#define CCOSH_HUGE 8.9884656743115795e+307 + const npy_double CCOSH_BIG = 22.0; + const npy_double CCOSH_HUGE = 8.9884656743115795e+307; #endif #if @precision@ >= 3 -#define CCOSH_BIG 24.0L -#define CCOSH_HUGE 5.94865747678615882543e+4931L +#if NPY_SIZEOF_LONGDOUBLE == NPY_SIZEOF_DOUBLE + const npy_longdouble CCOSH_BIG = 22.0L; + const npy_longdouble CCOSH_HUGE = 8.9884656743115795e+307L; +#else + const npy_longdouble CCOSH_BIG = 24.0L; + const npy_longdouble CCOSH_HUGE = 5.94865747678615882543e+4931L; +#endif #endif -@ctype@ npy_ccosh@c@(@ctype@ z) -{ @type@ x, y, h, absx; npy_int xfinite, yfinite; @@ -542,7 +547,7 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) /* |x| >= 22, so cosh(x) ~= exp(|x|) */ if (absx < SCALED_CEXP_LOWER@C@) { /* x < 710: exp(|x|) won't overflow */ - h = npy_exp@c@(absx) * 0.5; + h = npy_exp@c@(absx) * 0.5@c@; return npy_cpack@c@(h * npy_cos@c@(y), npy_copysign@c@(h, x) * npy_sin@c@(y)); } else if (absx < SCALED_CEXP_UPPER@C@) { @@ -633,22 +638,26 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) * Exceptional values are noted in the comments within the source code. * These values and the return value were taken from n1124.pdf. */ - +@ctype@ npy_csinh@c@(@ctype@ z) +{ #if @precision@ == 1 -#define CSINH_BIG 9.0f -#define CSINH_HUGE 1.70141183e+38f + const npy_float CSINH_BIG = 9.0f; + const npy_float CSINH_HUGE = 1.70141183e+38f; #endif #if @precision@ == 2 -#define CSINH_BIG 22.0 -#define CSINH_HUGE 8.9884656743115795e+307 + const npy_double CSINH_BIG = 22.0; + const npy_double CSINH_HUGE = 8.9884656743115795e+307; #endif #if @precision@ >= 3 -#define CSINH_BIG 24.0L -#define CSINH_HUGE 5.94865747678615882543e+4931L +#if NPY_SIZEOF_LONGDOUBLE == NPY_SIZEOF_DOUBLE + const npy_longdouble CSINH_BIG = 22.0L; + const npy_longdouble CSINH_HUGE = 8.9884656743115795e+307L; +#else + const npy_longdouble CSINH_BIG = 24.0L; + const npy_longdouble CSINH_HUGE = 5.94865747678615882543e+4931L; +#endif #endif -@ctype@ npy_csinh@c@(@ctype@ z) -{ @type@ x, y, h, absx; npy_int xfinite, yfinite; @@ -670,7 +679,7 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) /* |x| >= 22, so cosh(x) ~= exp(|x|) */ if (absx < SCALED_CEXP_LOWER@C@) { /* x < 710: exp(|x|) won't overflow */ - h = npy_exp@c@(npy_fabs@c@(x)) * 0.5; + h = npy_exp@c@(npy_fabs@c@(x)) * 0.5@c@; return npy_cpack@c@(npy_copysign@c@(h, x) * npy_cos@c@(y), h * npy_sin@c@(y)); } else if (x < SCALED_CEXP_UPPER@C@) { @@ -851,7 +860,7 @@ static @ctype@ _npy_scaled_cexp@c@(@type@ x, @type@ y, npy_int expt) /* Kahan's algorithm */ t = npy_tan@c@(y); - beta = 1.0 + t * t; /* = 1 / cos^2(y) */ + beta = 1 + t * t; /* = 1 / cos^2(y) */ s = npy_sinh@c@(x); rho = npy_sqrt@c@(1 + s * s); /* = cosh(x) */ denom = 1 + beta * s * s; @@ -939,17 +948,21 @@ static inline void _do_hard_work@c@(@type@ x, @type@ y, @type@ *rx, #if @precision@ == 1 const npy_float A_crossover = 10.0f; const npy_float B_crossover = 0.6417f; - const npy_float FOUR_SQRT_MIN = 0x1p-61f; + const npy_float FOUR_SQRT_MIN = 4.3368086899420177e-19f; #endif #if @precision@ == 2 const npy_double A_crossover = 10.0; const npy_double B_crossover = 0.6417; - const npy_double FOUR_SQRT_MIN = 0x1p-509; + const npy_double FOUR_SQRT_MIN = 5.9666725849601654e-154; #endif #if @precision@ == 3 const npy_longdouble A_crossover = 10.0l; const npy_longdouble B_crossover = 0.6417l; - const npy_longdouble FOUR_SQRT_MIN = 0x1p-8189l; +#if NPy_SIZEOF_LONGDOUBLE == NPY_SIZEOF_DOUBLE + const npy_longdouble FOUR_SQRT_MIN = 5.9666725849601654e-154; +#else + const npy_longdouble FOUR_SQRT_MIN = 7.3344154702193886625e-2466l; +#endif #endif @type@ R, S, A; /* A, B, R, and S are as in Hull et al. */ @type@ Am1, Amy; /* A-1, A-y. */ @@ -1068,16 +1081,21 @@ static inline void _clog_for_large_values@c@(@type@ x, @type@ y, @type@ *rr, @type@ *ri) { #if @precision@ == 1 - const npy_float QUARTER_SQRT_MAX = 0x1p61f; - const npy_float SQRT_MIN = 0x1p-63f; -#endif + const npy_float QUARTER_SQRT_MAX = 4.611685743549481e+18f; + const npy_float SQRT_MIN = 1.0842021724855044e-19f; + #endif #if @precision@ == 2 - const npy_double QUARTER_SQRT_MAX = 0x1p509; - const npy_double SQRT_MIN = 0x1p-511; -#endif + const npy_double QUARTER_SQRT_MAX = 3.3519519824856489e+153; + const npy_double SQRT_MIN = 1.4916681462400413e-154; + #endif #if @precision@ == 3 - const npy_longdouble QUARTER_SQRT_MAX = 0x1p8189l; - const npy_longdouble SQRT_MIN = 0x1p-8191l; +#if NPY_SIZEOF_LONGDOUBLE == NPY_SIZEOF_DOUBLE + const npy_longdouble QUARTER_SQRT_MAX = 3.3519519824856489e+153; + const npy_longdouble SQRT_MIN = 1.4916681462400413e-154; +#else + const npy_longdouble QUARTER_SQRT_MAX = 2.7268703390485398235e+2465l; + const npy_longdouble SQRT_MIN = 1.8336038675548471656e-2466l; +#endif #endif @type@ ax, ay, t; @@ -1146,7 +1164,7 @@ static inline void _clog_for_large_values@c@(@type@ x, @type@ y, if (npy_isnan(x) || npy_isnan(y)) { /* cacos(+-Inf + I*NaN) = NaN + I*opt(-)Inf */ if (npy_isinf(x)) - return npy_cpack@c@(y + y, -INFINITY); + return npy_cpack@c@(y + y, -NPY_INFINITY@C@); /* cacos(NaN + I*+-Inf) = NaN + I*-+Inf */ if (npy_isinf(y)) return npy_cpack@c@(x + x, -y); @@ -1158,7 +1176,7 @@ static inline void _clog_for_large_values@c@(@type@ x, @type@ y, * C99 leaves it optional whether to raise invalid if one of * the arguments is not NaN, so we opt not to raise it. */ - return npy_cpack@c@(x + 0.0L + (y + 0), x + 0.0L + (y + 0)); + return npy_cpack@c@(NPY_NAN@C@, NPY_NAN@C@); } if (ax > RECIP_EPSILON || ay > RECIP_EPSILON) { @@ -1294,7 +1312,7 @@ static inline void _clog_for_large_values@c@(@type@ x, @type@ y, * C99 leaves it optional whether to raise invalid if one of * the arguments is not NaN, so we opt not to raise it. */ - return npy_cpack@c@(x + 0.0L + (y + 0), x + 0.0L + (y + 0)); + return npy_cpack@c@(NPY_NAN@C@, NPY_NAN@C@); } if (ax > RECIP_EPSILON || ay > RECIP_EPSILON) { @@ -1502,7 +1520,7 @@ static inline npy_longdouble _real_part_reciprocall(npy_longdouble x, * C99 leaves it optional whether to raise invalid if one of * the arguments is not NaN, so we opt not to raise it. */ - return npy_cpack@c@(x + 0.0L + (y + 0), x + 0.0L + (y + 0)); + return npy_cpack@c@(NPY_NAN@C@, NPY_NAN@C@); } if (ax > RECIP_EPSILON || ay > RECIP_EPSILON) From be42cae7c6018b0bbeed325b444b7648c26ea36a Mon Sep 17 00:00:00 2001 From: Eric Moore Date: Fri, 11 Oct 2013 12:32:00 -0400 Subject: [PATCH 26/35] MAINT: fix ldexp/frexp changes to compile with MSVC. Regenerated loops.h. --- numpy/core/src/private/npy_config.h | 10 -- numpy/core/src/umath/loops.h | 173 +++++++++++++--------------- 2 files changed, 79 insertions(+), 104 deletions(-) diff --git a/numpy/core/src/private/npy_config.h b/numpy/core/src/private/npy_config.h index 453dbd065af9..69786fea4db7 100644 --- a/numpy/core/src/private/npy_config.h +++ b/numpy/core/src/private/npy_config.h @@ -10,16 +10,6 @@ #undef HAVE_HYPOT #endif -/* Safe to use ldexp and frexp for long double for MSVC builds */ -#if (NPY_SIZEOF_LONGDOUBLE == NPY_SIZEOF_DOUBLE) || defined(_MSC_VER) - #ifdef HAVE_LDEXP - #define HAVE_LDEXPL 1 - #endif - #ifdef HAVE_FREXP - #define HAVE_FREXPL 1 - #endif -#endif - /* Disable broken Sun Workshop Pro math functions */ #ifdef __SUNPRO_C #undef HAVE_ATAN2 diff --git a/numpy/core/src/umath/loops.h b/numpy/core/src/umath/loops.h index 62890f370b55..40b8532d09f1 100644 --- a/numpy/core/src/umath/loops.h +++ b/numpy/core/src/umath/loops.h @@ -1689,17 +1689,13 @@ HALF_sign(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(f NPY_NO_EXPORT void HALF_modf(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#ifdef HAVE_FREXPF NPY_NO_EXPORT void HALF_frexp(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#endif -#ifdef HAVE_LDEXPF NPY_NO_EXPORT void HALF_ldexp(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void HALF_ldexp_long(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#endif #define HALF_true_divide HALF_divide @@ -1843,17 +1839,13 @@ FLOAT_sign(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED( NPY_NO_EXPORT void FLOAT_modf(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#ifdef HAVE_FREXPF NPY_NO_EXPORT void FLOAT_frexp(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#endif -#ifdef HAVE_LDEXPF NPY_NO_EXPORT void FLOAT_ldexp(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void FLOAT_ldexp_long(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#endif #define FLOAT_true_divide FLOAT_divide @@ -1997,17 +1989,13 @@ DOUBLE_sign(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED NPY_NO_EXPORT void DOUBLE_modf(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#ifdef HAVE_FREXP NPY_NO_EXPORT void DOUBLE_frexp(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#endif -#ifdef HAVE_LDEXP NPY_NO_EXPORT void DOUBLE_ldexp(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void DOUBLE_ldexp_long(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#endif #define DOUBLE_true_divide DOUBLE_divide @@ -2151,17 +2139,13 @@ LONGDOUBLE_sign(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UN NPY_NO_EXPORT void LONGDOUBLE_modf(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#ifdef HAVE_FREXPL NPY_NO_EXPORT void LONGDOUBLE_frexp(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#endif -#ifdef HAVE_LDEXPL NPY_NO_EXPORT void LONGDOUBLE_ldexp(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void LONGDOUBLE_ldexp_long(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#endif #define LONGDOUBLE_true_divide LONGDOUBLE_divide @@ -2181,14 +2165,14 @@ LONGDOUBLE_ldexp_long(char **args, npy_intp *dimensions, npy_intp *steps, void * #define CEQ(xr,xi,yr,yi) (xr == yr && xi == yi); #define CNE(xr,xi,yr,yi) (xr != yr || xi != yi); -#line 316 +#line 312 -#line 322 +#line 318 NPY_NO_EXPORT void CFLOAT_add(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 322 +#line 318 NPY_NO_EXPORT void CFLOAT_subtract(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); @@ -2203,36 +2187,36 @@ CFLOAT_divide(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUS NPY_NO_EXPORT void CFLOAT_floor_divide(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 340 +#line 336 NPY_NO_EXPORT void CFLOAT_greater(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 340 +#line 336 NPY_NO_EXPORT void CFLOAT_greater_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 340 +#line 336 NPY_NO_EXPORT void CFLOAT_less(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 340 +#line 336 NPY_NO_EXPORT void CFLOAT_less_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 340 +#line 336 NPY_NO_EXPORT void CFLOAT_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 340 +#line 336 NPY_NO_EXPORT void CFLOAT_not_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 349 +#line 345 NPY_NO_EXPORT void CFLOAT_logical_and(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 349 +#line 345 NPY_NO_EXPORT void CFLOAT_logical_or(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); @@ -2242,15 +2226,15 @@ CFLOAT_logical_xor(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY NPY_NO_EXPORT void CFLOAT_logical_not(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 363 +#line 359 NPY_NO_EXPORT void CFLOAT_isnan(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 363 +#line 359 NPY_NO_EXPORT void CFLOAT_isinf(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 363 +#line 359 NPY_NO_EXPORT void CFLOAT_isfinite(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); @@ -2276,20 +2260,20 @@ CFLOAT__arg(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED NPY_NO_EXPORT void CFLOAT_sign(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 392 +#line 388 NPY_NO_EXPORT void CFLOAT_maximum(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 392 +#line 388 NPY_NO_EXPORT void CFLOAT_minimum(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 400 +#line 396 NPY_NO_EXPORT void CFLOAT_fmax(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 400 +#line 396 NPY_NO_EXPORT void CFLOAT_fmin(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); @@ -2297,14 +2281,14 @@ CFLOAT_fmin(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED #define CFLOAT_true_divide CFLOAT_divide -#line 316 +#line 312 -#line 322 +#line 318 NPY_NO_EXPORT void CDOUBLE_add(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 322 +#line 318 NPY_NO_EXPORT void CDOUBLE_subtract(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); @@ -2319,36 +2303,36 @@ CDOUBLE_divide(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNU NPY_NO_EXPORT void CDOUBLE_floor_divide(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 340 +#line 336 NPY_NO_EXPORT void CDOUBLE_greater(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 340 +#line 336 NPY_NO_EXPORT void CDOUBLE_greater_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 340 +#line 336 NPY_NO_EXPORT void CDOUBLE_less(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 340 +#line 336 NPY_NO_EXPORT void CDOUBLE_less_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 340 +#line 336 NPY_NO_EXPORT void CDOUBLE_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 340 +#line 336 NPY_NO_EXPORT void CDOUBLE_not_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 349 +#line 345 NPY_NO_EXPORT void CDOUBLE_logical_and(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 349 +#line 345 NPY_NO_EXPORT void CDOUBLE_logical_or(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); @@ -2358,15 +2342,15 @@ CDOUBLE_logical_xor(char **args, npy_intp *dimensions, npy_intp *steps, void *NP NPY_NO_EXPORT void CDOUBLE_logical_not(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 363 +#line 359 NPY_NO_EXPORT void CDOUBLE_isnan(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 363 +#line 359 NPY_NO_EXPORT void CDOUBLE_isinf(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 363 +#line 359 NPY_NO_EXPORT void CDOUBLE_isfinite(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); @@ -2392,20 +2376,20 @@ CDOUBLE__arg(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSE NPY_NO_EXPORT void CDOUBLE_sign(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 392 +#line 388 NPY_NO_EXPORT void CDOUBLE_maximum(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 392 +#line 388 NPY_NO_EXPORT void CDOUBLE_minimum(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 400 +#line 396 NPY_NO_EXPORT void CDOUBLE_fmax(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 400 +#line 396 NPY_NO_EXPORT void CDOUBLE_fmin(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); @@ -2413,14 +2397,14 @@ CDOUBLE_fmin(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSE #define CDOUBLE_true_divide CDOUBLE_divide -#line 316 +#line 312 -#line 322 +#line 318 NPY_NO_EXPORT void CLONGDOUBLE_add(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 322 +#line 318 NPY_NO_EXPORT void CLONGDOUBLE_subtract(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); @@ -2435,36 +2419,36 @@ CLONGDOUBLE_divide(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY NPY_NO_EXPORT void CLONGDOUBLE_floor_divide(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 340 +#line 336 NPY_NO_EXPORT void CLONGDOUBLE_greater(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 340 +#line 336 NPY_NO_EXPORT void CLONGDOUBLE_greater_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 340 +#line 336 NPY_NO_EXPORT void CLONGDOUBLE_less(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 340 +#line 336 NPY_NO_EXPORT void CLONGDOUBLE_less_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 340 +#line 336 NPY_NO_EXPORT void CLONGDOUBLE_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 340 +#line 336 NPY_NO_EXPORT void CLONGDOUBLE_not_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 349 +#line 345 NPY_NO_EXPORT void CLONGDOUBLE_logical_and(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 349 +#line 345 NPY_NO_EXPORT void CLONGDOUBLE_logical_or(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); @@ -2474,15 +2458,15 @@ CLONGDOUBLE_logical_xor(char **args, npy_intp *dimensions, npy_intp *steps, void NPY_NO_EXPORT void CLONGDOUBLE_logical_not(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 363 +#line 359 NPY_NO_EXPORT void CLONGDOUBLE_isnan(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 363 +#line 359 NPY_NO_EXPORT void CLONGDOUBLE_isinf(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 363 +#line 359 NPY_NO_EXPORT void CLONGDOUBLE_isfinite(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); @@ -2508,20 +2492,20 @@ CLONGDOUBLE__arg(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_U NPY_NO_EXPORT void CLONGDOUBLE_sign(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 392 +#line 388 NPY_NO_EXPORT void CLONGDOUBLE_maximum(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 392 +#line 388 NPY_NO_EXPORT void CLONGDOUBLE_minimum(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 400 +#line 396 NPY_NO_EXPORT void CLONGDOUBLE_fmax(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 400 +#line 396 NPY_NO_EXPORT void CLONGDOUBLE_fmin(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); @@ -2552,81 +2536,81 @@ TIMEDELTA_absolute(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY NPY_NO_EXPORT void TIMEDELTA_sign(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 433 +#line 429 NPY_NO_EXPORT void DATETIME__ones_like(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data)); -#line 441 +#line 437 NPY_NO_EXPORT void DATETIME_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 441 +#line 437 NPY_NO_EXPORT void DATETIME_not_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 441 +#line 437 NPY_NO_EXPORT void DATETIME_greater(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 441 +#line 437 NPY_NO_EXPORT void DATETIME_greater_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 441 +#line 437 NPY_NO_EXPORT void DATETIME_less(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 441 +#line 437 NPY_NO_EXPORT void DATETIME_less_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 449 +#line 445 NPY_NO_EXPORT void DATETIME_maximum(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 449 +#line 445 NPY_NO_EXPORT void DATETIME_minimum(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 433 +#line 429 NPY_NO_EXPORT void TIMEDELTA__ones_like(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data)); -#line 441 +#line 437 NPY_NO_EXPORT void TIMEDELTA_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 441 +#line 437 NPY_NO_EXPORT void TIMEDELTA_not_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 441 +#line 437 NPY_NO_EXPORT void TIMEDELTA_greater(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 441 +#line 437 NPY_NO_EXPORT void TIMEDELTA_greater_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 441 +#line 437 NPY_NO_EXPORT void TIMEDELTA_less(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 441 +#line 437 NPY_NO_EXPORT void TIMEDELTA_less_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 449 +#line 445 NPY_NO_EXPORT void TIMEDELTA_maximum(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 449 +#line 445 NPY_NO_EXPORT void TIMEDELTA_minimum(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); @@ -2691,27 +2675,27 @@ TIMEDELTA_mm_d_divide(char **args, npy_intp *dimensions, npy_intp *steps, void * ***************************************************************************** */ -#line 517 +#line 513 NPY_NO_EXPORT void OBJECT_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 517 +#line 513 NPY_NO_EXPORT void OBJECT_not_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 517 +#line 513 NPY_NO_EXPORT void OBJECT_greater(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 517 +#line 513 NPY_NO_EXPORT void OBJECT_greater_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 517 +#line 513 NPY_NO_EXPORT void OBJECT_less(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); -#line 517 +#line 513 NPY_NO_EXPORT void OBJECT_less_equal(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); @@ -2726,3 +2710,4 @@ OBJECT_sign(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED */ #endif + From ac075b26081f6063b5df9d04108563f1e542e912 Mon Sep 17 00:00:00 2001 From: Eric Moore Date: Tue, 15 Oct 2013 11:32:29 -0400 Subject: [PATCH 27/35] MAINT: npy_a(exp,log,sqrt,fabs) don't really exist. --- numpy/core/include/numpy/npy_math.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/numpy/core/include/numpy/npy_math.h b/numpy/core/include/numpy/npy_math.h index 357960113eed..9a8c8b407b92 100644 --- a/numpy/core/include/numpy/npy_math.h +++ b/numpy/core/include/numpy/npy_math.h @@ -118,10 +118,6 @@ double npy_tanh(double x); double npy_asin(double x); double npy_acos(double x); double npy_atan(double x); -double npy_aexp(double x); -double npy_alog(double x); -double npy_asqrt(double x); -double npy_afabs(double x); double npy_log(double x); double npy_log10(double x); From 79afefd71ef37d47bb47a0e5d8ba68e37551e358 Mon Sep 17 00:00:00 2001 From: Eric Moore Date: Wed, 16 Oct 2013 17:00:52 -0400 Subject: [PATCH 28/35] ENH: Run the test_c99complex.c tests with the test suite too. Some of the other tests in test_umath_complex.py are duplicates of these tests. --- numpy/core/setup.py | 10 + numpy/core/src/npymath/npymath_tests.c | 135 ++++ numpy/core/test_c99complex.c | 890 ++++++++++++++++--------- numpy/core/tests/test_umath_complex.py | 148 ++++ 4 files changed, 850 insertions(+), 333 deletions(-) create mode 100644 numpy/core/src/npymath/npymath_tests.c diff --git a/numpy/core/setup.py b/numpy/core/setup.py index aab7d0b0ca68..b9c5db13c21a 100644 --- a/numpy/core/setup.py +++ b/numpy/core/setup.py @@ -1018,6 +1018,16 @@ def get_dotblas_sources(ext, build_dir): config.add_extension('operand_flag_tests', sources = [join('src', 'umath', 'operand_flag_tests.c.src')]) + ####################################################################### + # npymath_tests module # + ####################################################################### + + config.add_extension('npymath_tests', + sources = [join('src', 'npymath', 'npymath_tests.c')], + depends = ['test_c99complex.c'], + libraries = ['npymath'] + ) + config.add_data_dir('tests') config.add_data_dir('tests/data') diff --git a/numpy/core/src/npymath/npymath_tests.c b/numpy/core/src/npymath/npymath_tests.c new file mode 100644 index 000000000000..2b6be50c7643 --- /dev/null +++ b/numpy/core/src/npymath/npymath_tests.c @@ -0,0 +1,135 @@ +#define NPY_NO_DEPRECATED_API NPY_API_VERSION + +#include "Python.h" + +#include "npy_pycompat.h" +#include "npy_config.h" +#include "numpy/npy_math.h" + +#define CACOS 1 +#define CASIN 1 +#define CATAN 1 +#define CACOSH 1 +#define CASINH 1 +#define CATANH 1 +#define CCOS 1 +#define CSIN 1 +#define CTAN 1 +#define CCOSH 1 +#define CSINH 1 +#define CTANH 1 +#define CEXP 1 +#define CLOG 1 +#define CPOW 1 +#define CSQRT 1 +#define HAVE_NUMPY 1 + +#define FLOAT 1 +#include "../../test_c99complex.c" +#undef FLOAT + +#define DOUBLE 1 +#include "../../test_c99complex.c" +#undef DOUBLE + +#define LONGDOUBLE 1 +#include "../../test_c99complex.c" +#undef LONGDOUBLE + +#define TESTFUNC_INT(func, suffix) \ + static PyObject * CONCAT3(_test_, func, suffix)(PyObject *NPY_UNUSED(self), PyObject *NPY_UNUSED(args)) \ + { \ + PyObject *errs; \ + errs = CONCAT3(test_, func, suffix)(); \ + if (errs == NULL) { \ + return errs; \ + } \ + if (PySequence_Size(errs) == 0) { \ + Py_DECREF(errs); \ + Py_INCREF(Py_None); \ + return Py_None; \ + } \ + else { \ + PyErr_SetObject(PyExc_AssertionError, errs); \ + return NULL; \ + } \ + } + +#define TESTFUNC(func) \ + TESTFUNC_INT(func, f) \ + TESTFUNC_INT(func, ) \ + TESTFUNC_INT(func, l) + +#define TESTMETHODDEF_INT(func, suffix) \ + {STRINGIZE(CONCAT3(test_, func, suffix)), CONCAT3(_test_, func, suffix), METH_VARARGS, ""} + +#define TESTMETHODDEF(func) \ + TESTMETHODDEF_INT(func, f), \ + TESTMETHODDEF_INT(func, ), \ + TESTMETHODDEF_INT(func, l) + +TESTFUNC(cacos) +TESTFUNC(casin) +TESTFUNC(catan) +TESTFUNC(cacosh) +TESTFUNC(casinh) +TESTFUNC(catanh) +TESTFUNC(ccos) +TESTFUNC(csin) +TESTFUNC(ctan) +TESTFUNC(ccosh) +TESTFUNC(csinh) +TESTFUNC(ctanh) +TESTFUNC(cexp) +TESTFUNC(clog) +TESTFUNC(cpow) +TESTFUNC(csqrt) + +static PyMethodDef methods[] = { + TESTMETHODDEF(cacos), + TESTMETHODDEF(casin), + TESTMETHODDEF(catan), + TESTMETHODDEF(cacosh), + TESTMETHODDEF(casinh), + TESTMETHODDEF(catanh), + TESTMETHODDEF(ccos), + TESTMETHODDEF(csin), + TESTMETHODDEF(ctan), + TESTMETHODDEF(ccosh), + TESTMETHODDEF(csinh), + TESTMETHODDEF(ctanh), + TESTMETHODDEF(cexp), + TESTMETHODDEF(clog), + TESTMETHODDEF(cpow), + TESTMETHODDEF(csqrt), + {NULL, NULL, 0, NULL} +}; + +#if defined(NPY_PY3K) +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "npymath_tests", + NULL, + -1, + methods, + NULL, + NULL, + NULL, + NULL +}; +#endif + +#if defined(NPY_PY3K) +PyMODINIT_FUNC PyInit_npymath_tests(void) +#else +PyMODINIT_FUNC +initnpymath_tests(void) +#endif +{ +#if defined(NPY_PY3K) + return PyModule_Create(&moduledef); +#else + Py_InitModule("npymath_tests", methods); +#endif +} + diff --git a/numpy/core/test_c99complex.c b/numpy/core/test_c99complex.c index bb841aabbd72..c1d6740252ba 100644 --- a/numpy/core/test_c99complex.c +++ b/numpy/core/test_c99complex.c @@ -4,11 +4,12 @@ #include #include -#define PYTESTPRECISION -#define PYTESTFUNC +#define PYTESTPRECISION 1 +#define PYTESTFUNC 1 #ifdef FLOAT #define TYPE float +#define CTYPE float complex #define SUFFIX f #define EPS FLT_EPSILON #define CLOSE_ATOL 0.0f @@ -16,27 +17,21 @@ #define BRANCH_SCALE 1e2f #define BRANCH_ATOL 1e-2f #define FMT "%.8e" -#define NPY_PI_2 1.570796326794896619231321691639751442f -#define NPY_PI 3.141592653589793238462643383279502884f -#define NPY_LOGE2 0.693147180559945309417232121458176568f -#define NPY_SQRT2 1.414213562373095048801688724209698079f #else #ifdef DOUBLE #define TYPE double -#define SUFFIX +#define CTYPE double complex +#define SUFFIX #define EPS DBL_EPSILON #define CLOSE_ATOL 0.0 #define CLOSE_RTOL 1e-12 #define BRANCH_SCALE 1e3 #define BRANCH_ATOL 1e-4 #define FMT "%.16e" -#define NPY_PI_2 1.570796326794896619231321691639751442 -#define NPY_PI 3.141592653589793238462643383279502884 -#define NPY_LOGE2 0.693147180559945309417232121458176568 -#define NPY_SQRT2 1.414213562373095048801688724209698079 #else #ifdef LONGDOUBLE #define TYPE long double +#define CTYPE long double complex #define SUFFIX l #define EPS LDBL_EPSILON #define CLOSE_ATOL 0.0l @@ -44,29 +39,282 @@ #define BRANCH_SCALE 1e3l #define BRANCH_ATOL 1e-4l #define FMT "%.18Le" -#define NPY_PI_2 1.570796326794896619231321691639751442L -#define NPY_PI 3.141592653589793238462643383279502884L -#define NPY_LOGE2 0.693147180559945309417232121458176568L -#define NPY_SQRT2 1.414213562373095048801688724209698079L #else #error "Define FLOAT or DOUBLE or LONGDOUBLE" #endif #endif #endif -const TYPE NZERO = -1.0 * 0.0; - #define STRINGIZE_INT(A) #A #define STRINGIZE(A) STRINGIZE_INT(A) #define CONCAT(A, B) A ## B #define ADDSUFFIX_INT(A, B) CONCAT(A, B) #define ADDSUFFIX(A) ADDSUFFIX_INT(A, SUFFIX) +#define ADDPREFIX(A) ADDSUFFIX_INT(PREFIX, A) + +#define CONCAT3(A, B, C) A ## B ## C +#define FUNC_INT(A, B, C) CONCAT3(A, B, C) +#define FUNC(A) FUNC_INT(PREFIX, A, SUFFIX) + +#ifdef HAVE_NUMPY +#include "Python.h" + +/* Use our versions no matter what. */ +#ifdef NAN +#undef NAN +#endif +#define NAN NPY_NAN + +#ifdef INFINITY +#undef INFINITY +#endif +#define INFINITY NPY_INFINITY + +#ifdef NZERO +#undef NZERO +#endif +#define NZERO NPY_NZERO + +/* Use the numpy types since we need them to call npy_math functions. */ +#undef TYPE +#undef CTYPE + +#ifdef FLOAT +#define TYPE npy_float +#define CTYPE npy_cfloat +#else +#ifdef DOUBLE +#define TYPE npy_double +#define CTYPE npy_cdouble +#else +#ifdef LONGDOUBLE +#define TYPE npy_longdouble +#define CTYPE npy_clongdouble +#endif +#endif +#endif + +#define PREFIX npy_ + +#define RETTYPE PyObject* + +#define INIT_FUNC() \ + PyObject *ret = PyList_New(0); \ + PyObject *entry; \ + const size_t bsize = 4096; \ + char buf[bsize]; \ + int used_size + +#define TEST_FAILED_INT(printexpr) \ + do { \ + used_size = printexpr; \ + if (used_size >= 0) { \ + entry = PyUString_FromStringAndSize(buf, used_size); \ + if (entry == NULL) { \ + Py_DECREF(ret); \ + return entry; \ + } \ + if (PyList_Append(ret, entry) == -1) { \ + Py_DECREF(entry); \ + Py_DECREF(ret); \ + return NULL; \ + } \ + }\ + else { \ + Py_DECREF(ret); \ + PyErr_SetString(PyExc_IOError, "PyOS_snprintf failed.");\ + } \ + } \ + while(0) + +#define TEST_FAILED(func, xr, xi, er, ei, rr, ri) \ + TEST_FAILED_INT(PyOS_snprintf(buf, bsize, "%d: " STRINGIZE(func) \ + "(" FMT " + " FMT "j): expected: " FMT " + " FMT "j: received: " FMT \ + " + " FMT "j\n", __LINE__, xr, xi, er, ei, rr, ri)) + +#define TEST_FAILED2(func, xr, xi, er1, ei1, er2, ei2, rr, ri) \ + TEST_FAILED_INT(PyOS_snprintf(buf, bsize, "%d: " STRINGIZE(func) \ + "(" FMT " + " FMT "j): expected: " FMT " + " FMT "j or " FMT " + " FMT \ + "j: received: " FMT " + " FMT "j\n", __LINE__, xr, xi, er1, ei1, \ + er2, ei2, rr, ri)) + +#define TEST_FAILED4(func, xr, xi, er1, ei1, er2, ei2, er3, ei3, er4, ei4, rr, ri) \ + TEST_FAILED_INT(PyOS_snprintf(buf, bsize, "%d: " STRINGIZE(func) \ + "(" FMT " + " FMT "j): expected: " FMT " + " FMT "j or " FMT " + " FMT \ + "j or " FMT " + " FMT "j or " FMT " + " FMT "j: received: " FMT " + " \ + FMT "j\n", __LINE__, xr, xi, er1, ei1, er2, ei2, er3, ei3, er4, ei4, \ + rr, ri)) + +#define TEST_CPOW_FAILED(func, xr, xi, yr, yi, er, ei, rr, ri) \ + TEST_FAILED_INT(PyOS_snprintf(buf, bsize, "%d: " STRINGIZE(func) "(" FMT \ + " + " FMT "j, " FMT " + " FMT "j): expected: " FMT " + " FMT \ + "j: received: " FMT " + " FMT "j\n", __LINE__, dxr, dxi, dyr, dyi, \ + der, dei, rr, ri)) + +#define TEST_BRANCH_CUT_FAILED(func, vxr, vxi, vdxr, vdxi, vrsign, visign, vcksignzero) \ + TEST_FAILED_INT(PyOS_snprintf(buf, bsize, STRINGIZE(func) \ + ": branch cut failure: x = " FMT " + " FMT "j, dx = " FMT " + " FMT \ + "j, rsign = %d, isign = %d, check_sign_zero = %d\n", vxr, vxi, \ + vdxr, vdxi, vrsign, visign, vcksignzero)) + +#define TEST_NEAR_CROSSOVER_FAILED(fname, j, k, zpr, zpi, czpr, czpi, zmr, zmi, czmr, czmi, diff, exmat)\ + TEST_FAILED_INT(PyOS_snprintf(buf, bsize, "%s: Loss of precision: j = %d, "\ + "k = %d\nzp = (" FMT " + " FMT "j) -> (" FMT " + " FMT "j)\nzm = (" FMT \ + " + " FMT "j) -> (" FMT " + " FMT "j)\ndiff = " FMT \ + ", exact match = %d\n", fname, j, k, zpr, zpi, czpr, czpi, zmr, zmi, \ + czmr, czmi, diff, exmat)) + +#define TEST_LOSS_OF_PRECISION_FAILED(fname, x, ratio) \ + TEST_FAILED_INT(PyOS_snprintf(buf, bsize, "%s: Loss of precision vs real:"\ + "\nx = " FMT "\nratio = " FMT "\n", fname, x, ratio)) + +#define TEST_LOSS_OF_PRECISION(cfunc, rfunc, real) \ + do { \ + PyObject *temp; \ + entry = ADDSUFFIX(check_loss_of_precision)(FUNC(cfunc), FUNC(rfunc), real, \ + STRINGIZE(FUNC(cfunc))); \ + if (!PySequence_Check(entry)) { \ + Py_DECREF(ret); \ + return entry; \ + } \ + temp = PySequence_Concat(ret, entry); \ + Py_DECREF(entry); \ + Py_DECREF(ret); \ + if (temp == NULL) { \ + return temp; \ + } else { \ + ret = temp; \ + temp = NULL; \ + } \ + entry = ADDSUFFIX(check_near_crossover)(FUNC(cfunc), STRINGIZE(FUNC(cfunc))); \ + if (!PySequence_Check(entry)) { \ + Py_DECREF(ret); \ + return entry; \ + } \ + temp = PySequence_Concat(ret, entry); \ + Py_DECREF(entry); \ + Py_DECREF(ret); \ + if (temp == NULL) { \ + return temp; \ + } else { \ + ret = temp; \ + temp = NULL; \ + } \ + } \ + while(0) + +#else +/* We assume that we have INFINITY and NAN */ + +#ifdef NZERO +#undef NZERO +#endif +#define NZERO (-1.0 * 0.0) + +/* copied from npy_math.h for when we don't have it. */ +#define NPY_E 2.718281828459045235360287471352662498 /* e */ +#define NPY_LOGE2 0.693147180559945309417232121458176568 /* log_e 2 */ +#define NPY_PI 3.141592653589793238462643383279502884 /* pi */ +#define NPY_PI_2 1.570796326794896619231321691639751442 /* pi/2 */ +#define NPY_SQRT2 1.414213562373095048801688724209698079 /* sqrt(2) */ + +#define NPY_Ef 2.718281828459045235360287471352662498F /* e */ +#define NPY_LOGE2f 0.693147180559945309417232121458176568F /* log_e 2 */ +#define NPY_PIf 3.141592653589793238462643383279502884F /* pi */ +#define NPY_PI_2f 1.570796326794896619231321691639751442F /* pi/2 */ +#define NPY_SQRT2f 1.414213562373095048801688724209698079F /* sqrt(2) */ + +#define NPY_El 2.718281828459045235360287471352662498L /* e */ +#define NPY_LOGE2l 0.693147180559945309417232121458176568L /* log_e 2 */ +#define NPY_PIl 3.141592653589793238462643383279502884L /* pi */ +#define NPY_PI_2l 1.570796326794896619231321691639751442L /* pi/2 */ +#define NPY_SQRT2l 1.414213562373095048801688724209698079L /* sqrt(2) */ + +#define PREFIX + +#define RETTYPE int + +#define INIT_FUNC() int ret = 1 + +#define TEST_FAILED(func, xr, xi, er, ei, rr, ri) \ + do { \ + ret = 0; \ + printf("%d: " STRINGIZE(func) "(" FMT " + " FMT "j): expected: " FMT \ + " + " FMT "j: received: " FMT " + " FMT "j\n", __LINE__, xr, xi, \ + er, ei, rr, ri); \ + } \ + while(0) + +#define TEST_FAILED2(func, xr, xi, er1, ei1, er2, ei2, rr, ri) \ + do { \ + ret = 0; \ + printf("%d: " STRINGIZE(func) "(" FMT " + " FMT "j): expected: " FMT \ + " + " FMT "j or " FMT " + " FMT "j: received: " FMT " + " FMT \ + "j\n", __LINE__, xr, xi, er1, ei1, er2, ei2, rr, ri); \ + } \ + while(0) + +#define TEST_FAILED4(func, xr, xi, er1, ei1, er2, ei2, er3, ei3, er4, ei4, rr, ri) \ + do { \ + ret = 0; \ + printf("%d: " STRINGIZE(func) "(" FMT " + " FMT "j): expected: " FMT \ + " + " FMT "j or " FMT " + " FMT "j or " FMT " + " FMT "j or " \ + FMT " + " FMT "j: received: " FMT " + " FMT "j\n", __LINE__, \ + xr, xi, er1, ei1, er2, ei2, er3, ei3, er4, ei4, rr, ri); \ + } \ + while(0) + +#define TEST_CPOW_FAILED(func, xr, xi, yr, yi, er, ei, rr, ri) \ + do { \ + ret = 0; \ + printf("%d: " STRINGIZE(func) "(" FMT " + " FMT "j, " FMT " + " FMT \ + "j): expected: " FMT " + " FMT "j: received: " FMT " + " FMT \ + "j\n", __LINE__, dxr, dxi, dyr, dyi, der, dei, rr, ri); \ + } \ + while(0) + +#define TEST_BRANCH_CUT_FAILED(func, vxr, vxi, vdxr, vdxi, vrsign, visign, vcksignzero) \ + do { \ + ret = 0; \ + printf(STRINGIZE(func) ": branch cut failure: x = " FMT " + " FMT \ + "j, dx = " FMT " + " FMT "j, rsign = %d, isign = %d, " \ + "check_sign_zero = %d\n", vxr, vxi, vdxr, vdxi, vrsign, visign, \ + vcksignzero); \ + } \ + while(0) -#define TEST_PRINTF(func, xr, xi, er, ei, rr, ri) \ - printf("%d: " STRINGIZE(func) STRINGIZE(SUFFIX) "(" FMT " + " FMT "j): " \ - "expected: " FMT " + " FMT "j: received: " FMT " + " FMT "j\n", \ - __LINE__, xr, xi, er, ei, rr, ri) +#define TEST_NEAR_CROSSOVER_FAILED(fname, j, k, zpr, zpi, czpr, czpi, zmr, zmi, czmr, czmi, diff, exmat)\ + do { \ + ret = 0; \ + printf("%s: Loss of precision: j = %d, k = %d\nzp = (" FMT " + " FMT \ + "j) -> (" FMT " + " FMT "j)\nzm = (" FMT " + " FMT "j) -> (" FMT \ + " + " FMT "j)\ndiff = " FMT ", exact match = %d\n", fname, j, k, \ + zpr, zpi, czpr, czpi, zmr, zmi, czmr, czmi, diff, exmat); \ + } \ + while(0) + +#define TEST_LOSS_OF_PRECISION_FAILED(fname, x, ratio) \ + do { \ + ret = 0; \ + printf("%s: Loss of precision vs real:\nx = " FMT "\nratio = " FMT "\n", \ + fname, x, ratio); \ + } \ + while(0) + +#define TEST_LOSS_OF_PRECISION(cfunc, rfunc, real) \ + do { \ + if (!ADDSUFFIX(check_loss_of_precision)(FUNC(cfunc), FUNC(rfunc), real, \ + STRINGIZE(FUNC(cfunc)))) { \ + ret = 0; \ + } \ + if (!ADDSUFFIX(check_near_crossover)(FUNC(cfunc), STRINGIZE(FUNC(cfunc)))) { \ + ret = 0; \ + } \ + } \ + while(0) + +#endif #define TEST_INT(func, xr, xi, er, ei, rtest, itest) \ do { \ @@ -74,13 +322,12 @@ const TYPE NZERO = -1.0 * 0.0; TYPE dxi = xi; \ TYPE der = er; \ TYPE dei = ei; \ - TYPE complex x = cpack(dxr, dxi); \ - TYPE complex r = ADDSUFFIX(func)(x); \ - TYPE rr = ADDSUFFIX(creal)(r); \ - TYPE ri = ADDSUFFIX(cimag)(r); \ - if (!(rtest(rr, der) && itest(ri, dei))) { \ - ret = 0; \ - TEST_PRINTF(func, dxr, dxi, der, dei, rr, ri); \ + CTYPE x = FUNC(cpack)(dxr, dxi); \ + CTYPE r = FUNC(func)(x); \ + TYPE rr = FUNC(creal)(r); \ + TYPE ri = FUNC(cimag)(r); \ + if (!(ADDSUFFIX(rtest)(rr, der) && ADDSUFFIX(itest)(ri, dei))) { \ + TEST_FAILED(FUNC(func), dxr, dxi, der, dei, rr, ri); \ } \ } \ while(0) @@ -105,16 +352,13 @@ const TYPE NZERO = -1.0 * 0.0; TYPE dei1 = ei1; \ TYPE der2 = er2; \ TYPE dei2 = ei2; \ - TYPE complex x = cpack(dxr, dxi); \ - TYPE complex r = ADDSUFFIX(func)(x); \ - TYPE rr = ADDSUFFIX(creal)(r); \ - TYPE ri = ADDSUFFIX(cimag)(r); \ - if (!((rtest(rr, der1) && itest(ri, dei1)) || \ - (rtest(rr, der2) && itest(ri, dei2)))) { \ - ret = 0; \ - TEST_PRINTF(func, dxr, dxi, der1, dei1, rr, ri); \ - printf("or"); \ - TEST_PRINTF(func, dxr, dxi, der2, dei2, rr, ri); \ + CTYPE x = FUNC(cpack)(dxr, dxi); \ + CTYPE r = FUNC(func)(x); \ + TYPE rr = FUNC(creal)(r); \ + TYPE ri = FUNC(cimag)(r); \ + if (!((ADDSUFFIX(rtest)(rr, der1) && ADDSUFFIX(itest)(ri, dei1)) || \ + (ADDSUFFIX(rtest)(rr, der2) && ADDSUFFIX(itest)(ri, dei2)))) { \ + TEST_FAILED2(FUNC(func), dxr, dxi, der1, dei1, der2, dei2, rr, ri);\ } \ } \ while(0) @@ -140,22 +384,15 @@ const TYPE NZERO = -1.0 * 0.0; TYPE dei3 = ei3; \ TYPE der4 = er4; \ TYPE dei4 = ei4; \ - TYPE complex x = cpack(dxr, dxi); \ - TYPE complex r = ADDSUFFIX(func)(x); \ - TYPE rr = ADDSUFFIX(creal)(r); \ - TYPE ri = ADDSUFFIX(cimag)(r); \ - if (!((isequal(rr, der1) && isequal(ri, dei1)) || \ - (isequal(rr, der2) && isequal(ri, dei2)) || \ - (isequal(rr, der3) && isequal(ri, dei3)) || \ - (isequal(rr, der4) && isequal(ri, dei4)))) { \ - ret = 0; \ - TEST_PRINTF(func, dxr, dxi, der1, dei1, rr, ri); \ - printf("or"); \ - TEST_PRINTF(func, dxr, dxi, der2, dei2, rr, ri); \ - printf("or"); \ - TEST_PRINTF(func, dxr, dxi, der3, dei3, rr, ri); \ - printf("or"); \ - TEST_PRINTF(func, dxr, dxi, der4, dei4, rr, ri); \ + CTYPE x = FUNC(cpack)(dxr, dxi); \ + CTYPE r = FUNC(func)(x); \ + TYPE rr = FUNC(creal)(r); \ + TYPE ri = FUNC(cimag)(r); \ + if (!((ADDSUFFIX(isequal)(rr, der1) && ADDSUFFIX(isequal)(ri, dei1)) ||\ + (ADDSUFFIX(isequal)(rr, der2) && ADDSUFFIX(isequal)(ri, dei2)) ||\ + (ADDSUFFIX(isequal)(rr, der3) && ADDSUFFIX(isequal)(ri, dei3)) ||\ + (ADDSUFFIX(isequal)(rr, der4) && ADDSUFFIX(isequal)(ri, dei4)))){\ + TEST_FAILED4(FUNC(func), dxr, dxi, der1, dei1, der2, dei2, der3, dei3, der4, dei4, rr, ri); \ } \ } \ while(0) @@ -168,17 +405,13 @@ const TYPE NZERO = -1.0 * 0.0; TYPE dyi = yi; \ TYPE der = er; \ TYPE dei = ei; \ - TYPE complex x = cpack(xr, xi); \ - TYPE complex y = cpack(yr, yi); \ - TYPE complex r = ADDSUFFIX(cpow)(x, y); \ - TYPE rr = ADDSUFFIX(creal)(r); \ - TYPE ri = ADDSUFFIX(cimag)(r); \ - if (!(test(rr, der) && test(ri, dei))) { \ - ret = 0; \ - printf("%d: " STRINGIZE(cpow) STRINGIZE(SUFFIX) "(" FMT " + " FMT \ - "j, " FMT " + " FMT "j): expected: " FMT " + " FMT \ - "j: received: " FMT " + " FMT "j\n", __LINE__, dxr, dxi, \ - dyr, dyi, der, dei, rr, ri); \ + CTYPE x = FUNC(cpack)(xr, xi); \ + CTYPE y = FUNC(cpack)(yr, yi); \ + CTYPE r = FUNC(cpow)(x, y); \ + TYPE rr = FUNC(creal)(r); \ + TYPE ri = FUNC(cimag)(r); \ + if (!(ADDSUFFIX(test)(rr, der) && ADDSUFFIX(test)(ri, dei))) { \ + TEST_CPOW_FAILED(FUNC(cpow), dxr, dxi, dyr, dyi, der, dei, rr, ri);\ } \ } \ while(0) @@ -196,17 +429,17 @@ const TYPE NZERO = -1.0 * 0.0; TYPE dxi = xi; \ TYPE der = er; \ TYPE dei = ei; \ - TYPE complex r; \ - TYPE complex x = cpack(xr, xi); \ + CTYPE r; \ + CTYPE x = FUNC(cpack)(xr, xi); \ TYPE rr, ri; \ feclearexcept(FE_ALL_EXCEPT); \ - r = ADDSUFFIX(func)(x); \ + r = FUNC(func)(x); \ except = fetestexcept(fpe); \ - rr = ADDSUFFIX(creal)(r); \ - ri = ADDSUFFIX(cimag)(r); \ - if (!(except & fpe && isequal(rr, der) && isequal(ri, dei))) { \ - ret = 0; \ - TEST_PRINTF(func, dxr, dxi, der, dei, rr, ri); \ + rr = FUNC(creal)(r); \ + ri = FUNC(cimag)(r); \ + if (!(except & fpe && ADDSUFFIX(isequal)(rr, der) \ + && ADDSUFFIX(isequal)(ri, dei))) { \ + TEST_FAILED(FUNC(func), dxr, dxi, der, dei, rr, ri); \ } \ } \ while(0) @@ -220,23 +453,21 @@ const TYPE NZERO = -1.0 * 0.0; TYPE dei1 = ei1; \ TYPE der2 = er2; \ TYPE dei2 = ei2; \ - TYPE complex r; \ - TYPE complex x = cpack(xr, xi); \ + CTYPE r; \ + CTYPE x = FUNC(cpack)(xr, xi); \ TYPE rr, ri; \ feclearexcept(FE_ALL_EXCEPT); \ - r = ADDSUFFIX(func)(x); \ + r = FUNC(func)(x); \ except = fetestexcept(fpe); \ - rr = ADDSUFFIX(creal)(r); \ - ri = ADDSUFFIX(cimag)(r); \ - if (!(except & fpe && ((isequal(rr, der1) && isequal(ri, dei1)) \ - || (isequal(rr, der2) && isequal(ri, dei2))))) {\ - ret = 0; \ - TEST_PRINTF(func, dxr, dxi, der1, dei1, rr, ri); \ - printf("or"); \ - TEST_PRINTF(func, dxr, dxi, der2, dei2, rr, ri); \ + rr = FUNC(creal)(r); \ + ri = FUNC(cimag)(r); \ + if (!(except & fpe && \ + ((ADDSUFFIX(isequal)(rr, der1) && ADDSUFFIX(isequal)(ri, dei1)) || \ + (ADDSUFFIX(isequal)(rr, der2) && ADDSUFFIX(isequal)(ri, dei2))))) {\ + TEST_FAILED2(FUNC(func), dxr, dxi, der1, dei1, der2, dei2, rr, ri);\ } \ } \ - while(0) + while(0) #define TEST_BRANCH_CUT(func, xr, xi, dxr, dxi, rsign, isign, cksignzero) \ do { \ @@ -247,37 +478,18 @@ const TYPE NZERO = -1.0 * 0.0; int vrsign = rsign; \ int visign = isign; \ int vcksignzero = cksignzero; \ - TYPE complex x = cpack(vxr, vxi); \ - TYPE complex dx = cpack(vdxr, vdxi); \ - int q = check_branch_cut(ADDSUFFIX(func), x, dx, \ + int q = ADDSUFFIX(check_branch_cut)(FUNC(func), vxr, vxi, vdxr, vdxi, \ vrsign, visign, vcksignzero); \ if (!q) { \ - ret = 0; \ - printf(STRINGIZE(func) STRINGIZE(SUFFIX) ": branch cut failure: " \ - "x = " FMT " + " FMT "j, dx = " FMT " + " FMT \ - "j, rsign = %d, isign = %d, check_sign_zero = %d\n", vxr, \ - vxi, vdxr, vdxi, vrsign, visign, vcksignzero); \ - } \ - } \ - while(0) - -#define TEST_LOSS_OF_PRECISION(cfunc, rfunc, real) \ - do { \ - if (!check_loss_of_precision(ADDSUFFIX(cfunc), ADDSUFFIX(rfunc), real, \ - STRINGIZE(cfunc) STRINGIZE(SUFFIX))) { \ - ret = 0; \ - } \ - if (!check_near_crossover(ADDSUFFIX(cfunc), \ - STRINGIZE(cfunc) STRINGIZE(SUFFIX))) { \ - ret = 0; \ + TEST_BRANCH_CUT_FAILED(FUNC(func), vxr, vxi, vdxr, vdxi, vrsign, visign, vcksignzero); \ } \ } \ while(0) -TYPE complex cpack(TYPE r, TYPE i) +CTYPE ADDSUFFIX(cpack)(TYPE r, TYPE i) { union { - TYPE complex z; + CTYPE z; TYPE a[2]; } z1; z1.a[0] = r; @@ -285,30 +497,30 @@ TYPE complex cpack(TYPE r, TYPE i) return z1.z; } -int isclose(TYPE a, TYPE b) +static int ADDSUFFIX(isclose)(TYPE a, TYPE b) { const TYPE atol = CLOSE_ATOL; const TYPE rtol = CLOSE_RTOL; - if (isfinite(a) && isfinite(b)) { - return (ADDSUFFIX(fabs)(a - b) <= (atol + rtol*ADDSUFFIX(fabs)(b))); + if (ADDPREFIX(isfinite)(a) && ADDPREFIX(isfinite)(b)) { + return (FUNC(fabs)(a - b) <= (atol + rtol*FUNC(fabs)(b))); } return 0; } -int isequal(TYPE a, TYPE b) +static int ADDSUFFIX(isequal)(TYPE a, TYPE b) { - if (isfinite(a) && isfinite(b)) { + if (ADDPREFIX(isfinite)(a) && ADDPREFIX(isfinite)(b)) { if (a == 0 && b == 0) { - TYPE signa = ADDSUFFIX(copysign)(1.0, a); - TYPE signb = ADDSUFFIX(copysign)(1.0, b); + TYPE signa = FUNC(copysign)(1.0, a); + TYPE signb = FUNC(copysign)(1.0, b); return signa == signb; } else { return a == b; } } - else if (isnan(a) && isnan(b)) { + else if (ADDPREFIX(isnan)(a) && ADDPREFIX(isnan)(b)) { return 1; } else {/* infs */ @@ -316,66 +528,73 @@ int isequal(TYPE a, TYPE b) } } -typedef TYPE complex (*complexfunc)(TYPE complex); -typedef TYPE (*realfunc)(TYPE); +#if defined(CACOS) || defined(CASIN) || defined(CATAN) || defined(CACOSH) || defined(CASINH) || defined(CATANH) || defined(CLOG) || defined(CSQRT) +typedef CTYPE (*ADDSUFFIX(complexfunc))(CTYPE); +typedef TYPE (*ADDSUFFIX(realfunc))(TYPE); -int check_branch_cut(complexfunc cfunc, TYPE complex x0, TYPE complex dx, +static int ADDSUFFIX(check_branch_cut)(ADDSUFFIX(complexfunc) cfunc, TYPE x0r, TYPE x0i, TYPE dxr, TYPE dxi, int re_sign, int im_sign, int sig_zero_ok) { const TYPE scale = EPS * BRANCH_SCALE; const TYPE atol = BRANCH_ATOL; - TYPE complex shift = dx*scale*ADDSUFFIX(cabs)(x0)/ADDSUFFIX(cabs)(dx); - TYPE complex y0 = cfunc(x0); - TYPE complex yp = cfunc(x0 + shift); - TYPE complex ym = cfunc(x0 - shift); + TYPE scale2 = FUNC(cabs)(FUNC(cpack)(x0r, x0i)) / FUNC(cabs)(FUNC(cpack)(dxr, dxi)); + + TYPE shiftr = dxr*scale*scale2; + TYPE shifti = dxi*scale*scale2; + CTYPE y0 = cfunc(FUNC(cpack)(x0r, x0i)); + CTYPE yp = cfunc(FUNC(cpack)(x0r + shiftr, x0i + shifti)); + CTYPE ym = cfunc(FUNC(cpack)(x0r - shiftr, x0i - shifti)); + CTYPE x0; TYPE y0r, y0i, ypr, ypi, ymr, ymi; - y0r = ADDSUFFIX(creal)(y0); - y0i = ADDSUFFIX(cimag)(y0); - ypr = ADDSUFFIX(creal)(yp); - ypi = ADDSUFFIX(cimag)(yp); - ymr = ADDSUFFIX(creal)(ym); - ymi = ADDSUFFIX(cimag)(ym); + y0r = FUNC(creal)(y0); + y0i = FUNC(cimag)(y0); + ypr = FUNC(creal)(yp); + ypi = FUNC(cimag)(yp); + ymr = FUNC(creal)(ym); + ymi = FUNC(cimag)(ym); - if (ADDSUFFIX(fabs)(y0r - ypr) >= atol) + if (FUNC(fabs)(y0r - ypr) >= atol) return 0; - if (ADDSUFFIX(fabs)(y0i - ypi) >= atol) + if (FUNC(fabs)(y0i - ypi) >= atol) return 0; - if (ADDSUFFIX(fabs)(y0r - re_sign*ymr) >= atol) + if (FUNC(fabs)(y0r - re_sign*ymr) >= atol) return 0; - if (ADDSUFFIX(fabs)(y0i - im_sign*ymi) >= atol) + if (FUNC(fabs)(y0i - im_sign*ymi) >= atol) return 0; - if (sig_zero_ok) { - if (ADDSUFFIX(creal)(x0) == 0 && ADDSUFFIX(creal)(dx) != 0) { - x0 = cpack(NZERO, ADDSUFFIX(cimag)(x0)); + if (sig_zero_ok) { + if (x0r == 0 && dxr != 0) { + x0 = FUNC(cpack)(NZERO, x0i); ym = cfunc(x0); - ymr = ADDSUFFIX(creal)(ym); - ymi = ADDSUFFIX(cimag)(ym); - if (ADDSUFFIX(fabs)(y0r - re_sign*ymr) >= atol) + ymr = FUNC(creal)(ym); + ymi = FUNC(cimag)(ym); + if (FUNC(fabs)(y0r - re_sign*ymr) >= atol) return 0; - if (ADDSUFFIX(fabs)(y0i - im_sign*ymi) >= atol) + if (FUNC(fabs)(y0i - im_sign*ymi) >= atol) return 0; } - else if (ADDSUFFIX(cimag)(x0) == 0 && ADDSUFFIX(cimag)(dx) != 0) { - x0 = cpack(ADDSUFFIX(creal)(x0), NZERO); + else if (x0i == 0 && dxi != 0) { + x0 = FUNC(cpack)(x0r, NZERO); ym = cfunc(x0); - ymr = ADDSUFFIX(creal)(ym); - ymi = ADDSUFFIX(cimag)(ym); - if (ADDSUFFIX(fabs)(y0r - re_sign*ymr) >= atol) + ymr = FUNC(creal)(ym); + ymi = FUNC(cimag)(ym); + if (FUNC(fabs)(y0r - re_sign*ymr) >= atol) return 0; - if (ADDSUFFIX(fabs)(y0i - im_sign*ymi) >= atol) + if (FUNC(fabs)(y0i - im_sign*ymi) >= atol) return 0; - } + } } return 1; } +#endif -int check_near_crossover(complexfunc cfunc, const char* fname) +#if defined(CASIN) || defined(CATAN) || defined(CASINH) || defined(CATANH) +static RETTYPE ADDSUFFIX(check_near_crossover)(ADDSUFFIX(complexfunc) cfunc, const char* fname) { const TYPE x = 1e-3; const int rpnt[] = {-1, -1, -1, 0, 0, 1, 1, 1}; @@ -385,57 +604,55 @@ int check_near_crossover(complexfunc cfunc, const char* fname) const int di[] = {0, 1, 1}; const int ndr = sizeof(dr) / sizeof(int); int k, j; - int ret = 1; + int equal; TYPE drj, dij, diff; - TYPE complex zp, zm, czp, czm; + CTYPE zp, zm, czp, czm; + INIT_FUNC(); for (j = 0; j < ndr; j++) { drj = 2 * x * dr[j] * EPS; dij = 2 * x * di[j] * EPS; for (k = 0; k < npnt; k++) { - zp = cpack(x*rpnt[k] + drj, x*ipnt[k] + dij); - zm = cpack(x*rpnt[k] - drj, x*ipnt[k] - dij); + zp = FUNC(cpack)(x*rpnt[k] + drj, x*ipnt[k] + dij); + zm = FUNC(cpack)(x*rpnt[k] - drj, x*ipnt[k] - dij); czp = cfunc(zp); czm = cfunc(zm); - diff = ADDSUFFIX(cabs)(czp - czm); - if ( diff > 2*EPS || czp == czm) { - printf("%s: Loss of precision: j = %d, k = %d\n",fname, j, k); - printf("zp = (" FMT " + " FMT "j) -> (" FMT " + " FMT "j)\n", \ - ADDSUFFIX(creal)(zp), ADDSUFFIX(cimag)(zp), \ - ADDSUFFIX(creal)(czp), ADDSUFFIX(cimag)(czp)); - printf("zm = (" FMT " + " FMT "j) -> (" FMT " + " FMT "j)\n", \ - ADDSUFFIX(creal)(zm), ADDSUFFIX(cimag)(zm), \ - ADDSUFFIX(creal)(czm), ADDSUFFIX(cimag)(czm)); - printf("diff = " FMT ", exact match = %d\n", diff, czp == czm); - ret = 0; + diff = FUNC(cabs)(FUNC(cpack)(FUNC(creal)(czp) - FUNC(creal)(czm),\ + FUNC(cimag)(czp) - FUNC(cimag)(czm))); + equal = (FUNC(creal)(czp) == FUNC(creal)(czm)) && (FUNC(cimag)(czp) == FUNC(cimag)(czm)); + if ( diff > 2*EPS || equal) { + TEST_NEAR_CROSSOVER_FAILED(fname, j, k, FUNC(creal)(zp), \ + FUNC(cimag)(zp), FUNC(creal)(czp), FUNC(cimag)(czp), \ + FUNC(creal)(zm), FUNC(cimag)(zm), FUNC(creal)(czm), \ + FUNC(cimag)(czm), diff, equal); } } } return ret; } -int clp_internal(complexfunc cfunc, realfunc rfunc, int real, TYPE x) +static int ADDSUFFIX(clp_internal)(ADDSUFFIX(complexfunc) cfunc, ADDSUFFIX(realfunc) rfunc, int real, TYPE x) { TYPE num = rfunc(x); TYPE den; - TYPE complex z; + CTYPE z; if (real == 1) { - z = cpack(x, 0); + z = FUNC(cpack)(x, 0); z = cfunc(z); - den = ADDSUFFIX(creal)(z); + den = FUNC(creal)(z); } else { - z = cpack(0, x); + z = FUNC(cpack)(0, x); z = cfunc(z); - den = ADDSUFFIX(cimag)(z); + den = FUNC(cimag)(z); } - return ADDSUFFIX(fabs)(num/den - 1); + return FUNC(fabs)(num/den - 1); } -int check_loss_of_precision(complexfunc cfunc, realfunc rfunc, int real, +static RETTYPE ADDSUFFIX(check_loss_of_precision)(ADDSUFFIX(complexfunc) cfunc, ADDSUFFIX(realfunc) rfunc, int real, const char* fname) { const int n_series = 200; @@ -452,63 +669,58 @@ int check_loss_of_precision(complexfunc cfunc, realfunc rfunc, int real, TYPE x, ratio; int k; - int ret = 1; + INIT_FUNC(); for(k = 0; k < n_series; k++) { - x = ADDSUFFIX(pow)(10.0, xsb + k*dxs); - ratio = clp_internal(cfunc, rfunc, real, x); + x = FUNC(pow)(10.0, xsb + k*dxs); + ratio = ADDSUFFIX(clp_internal)(cfunc, rfunc, real, x); if (ratio > rtol) { - printf("%s: Loss of precision vs real:\n", fname); - printf("x = " FMT "\n", x); - printf("ratio = " FMT "\n", ratio); - ret = 0; + TEST_LOSS_OF_PRECISION_FAILED(fname, x, ratio); } } for(k = 0; k < n_basic; k++) { - x = ADDSUFFIX(pow)(10.0, xbb + k*dxb); - ratio = clp_internal(cfunc, rfunc, real, x); + x = FUNC(pow)(10.0, xbb + k*dxb); + ratio = ADDSUFFIX(clp_internal)(cfunc, rfunc, real, x); if (ratio > rtol) { - printf("%s: Loss of precision vs. real:\n", fname); - printf("x = " FMT "\n", x); - printf("ratio = " FMT "\n", ratio); - ret = 0; + TEST_LOSS_OF_PRECISION_FAILED(fname, x, ratio); } } return ret; } +#endif #ifdef CACOS -int test_cacos() +RETTYPE ADDSUFFIX(test_cacos)(void) { - int ret = 1; + INIT_FUNC(); /* cacos(conj(z)) = conj(cacos(z)) */ - TEST_CE(cacos, 0, 0, NPY_PI_2, NZERO); - TEST_CE(cacos, 0, NZERO, NPY_PI_2, 0); + TEST_CE(cacos, 0, 0, ADDSUFFIX(NPY_PI_2), NZERO); + TEST_CE(cacos, 0, NZERO, ADDSUFFIX(NPY_PI_2), 0); - TEST_CE(cacos, NZERO, 0, NPY_PI_2, NZERO); - TEST_CE(cacos, NZERO, NZERO, NPY_PI_2, 0); + TEST_CE(cacos, NZERO, 0, ADDSUFFIX(NPY_PI_2), NZERO); + TEST_CE(cacos, NZERO, NZERO, ADDSUFFIX(NPY_PI_2), 0); - TEST_CE(cacos, 0, NAN, NPY_PI_2, NAN); - TEST_CE(cacos, NZERO, NAN, NPY_PI_2, NAN); + TEST_CE(cacos, 0, NAN, ADDSUFFIX(NPY_PI_2), NAN); + TEST_CE(cacos, NZERO, NAN, ADDSUFFIX(NPY_PI_2), NAN); - TEST_CE(cacos, 2.0, INFINITY, NPY_PI_2, -INFINITY); - TEST_CE(cacos, 2.0, -INFINITY, NPY_PI_2, INFINITY); + TEST_CE(cacos, 2.0, INFINITY, ADDSUFFIX(NPY_PI_2), -INFINITY); + TEST_CE(cacos, 2.0, -INFINITY, ADDSUFFIX(NPY_PI_2), INFINITY); /* can raise FE_INVALID or not */ TEST_EE(cacos, 2.0, NAN, NAN, NAN); - TEST_CE(cacos, -INFINITY, 2.0, NPY_PI, -INFINITY); - TEST_CE(cacos, -INFINITY, -2.0, NPY_PI, INFINITY); + TEST_CE(cacos, -INFINITY, 2.0, ADDSUFFIX(NPY_PI), -INFINITY); + TEST_CE(cacos, -INFINITY, -2.0, ADDSUFFIX(NPY_PI), INFINITY); TEST_EE(cacos, INFINITY, 2.0, 0, -INFINITY); TEST_EE(cacos, INFINITY, -2.0, 0, INFINITY); - TEST_CE(cacos, -INFINITY, INFINITY, 0.75 * NPY_PI, -INFINITY); - TEST_CE(cacos, -INFINITY, -INFINITY, 0.75 * NPY_PI, INFINITY); + TEST_CE(cacos, -INFINITY, INFINITY, 0.75 * ADDSUFFIX(NPY_PI), -INFINITY); + TEST_CE(cacos, -INFINITY, -INFINITY, 0.75 * ADDSUFFIX(NPY_PI), INFINITY); - TEST_CE(cacos, INFINITY, INFINITY, 0.25 * NPY_PI, -INFINITY); - TEST_CE(cacos, INFINITY, -INFINITY, 0.25 * NPY_PI, INFINITY); + TEST_CE(cacos, INFINITY, INFINITY, 0.25 * ADDSUFFIX(NPY_PI), -INFINITY); + TEST_CE(cacos, INFINITY, -INFINITY, 0.25 * ADDSUFFIX(NPY_PI), INFINITY); /* sign of imaginary part is unspecified. */ TEST_UNSPECIFIED2(cacos, INFINITY, NAN, NAN, INFINITY, NAN, -INFINITY); @@ -535,9 +747,9 @@ int test_cacos() #endif #ifdef CASIN -int test_casin() +RETTYPE ADDSUFFIX(test_casin)(void) { - int ret = 1; + INIT_FUNC(); /* casin(conj(z)) = conj(casin(z)) and casin is odd */ TEST_EE(casin, 0, 0, 0, 0); @@ -545,10 +757,10 @@ int test_casin() TEST_EE(casin, NZERO, 0, NZERO, 0); TEST_EE(casin, NZERO, NZERO, NZERO, NZERO); - TEST_CE(casin, -INFINITY, 2.0, -NPY_PI_2, INFINITY); - TEST_CE(casin, INFINITY, 2.0, NPY_PI_2, INFINITY); - TEST_CE(casin, -INFINITY, -2.0, -NPY_PI_2, -INFINITY); - TEST_CE(casin, INFINITY, -2.0, NPY_PI_2, -INFINITY); + TEST_CE(casin, -INFINITY, 2.0, -ADDSUFFIX(NPY_PI_2), INFINITY); + TEST_CE(casin, INFINITY, 2.0, ADDSUFFIX(NPY_PI_2), INFINITY); + TEST_CE(casin, -INFINITY, -2.0, -ADDSUFFIX(NPY_PI_2), -INFINITY); + TEST_CE(casin, INFINITY, -2.0, ADDSUFFIX(NPY_PI_2), -INFINITY); /* can raise FE_INVALID or not */ TEST_EE(casin, NAN, -2.0, NAN, NAN); @@ -559,10 +771,10 @@ int test_casin() TEST_EE(casin, -2.0, -INFINITY, NZERO, -INFINITY); TEST_EE(casin, 2.0, -INFINITY, 0, -INFINITY); - TEST_CE(casin, -INFINITY, INFINITY, -0.25*NPY_PI, INFINITY); - TEST_CE(casin, INFINITY, INFINITY, 0.25*NPY_PI, INFINITY); - TEST_CE(casin, -INFINITY, -INFINITY, -0.25*NPY_PI, -INFINITY); - TEST_CE(casin, INFINITY, -INFINITY, 0.25*NPY_PI, -INFINITY); + TEST_CE(casin, -INFINITY, INFINITY, -0.25*ADDSUFFIX(NPY_PI), INFINITY); + TEST_CE(casin, INFINITY, INFINITY, 0.25*ADDSUFFIX(NPY_PI), INFINITY); + TEST_CE(casin, -INFINITY, -INFINITY, -0.25*ADDSUFFIX(NPY_PI), -INFINITY); + TEST_CE(casin, INFINITY, -INFINITY, 0.25*ADDSUFFIX(NPY_PI), -INFINITY); TEST_EE(casin, NAN, INFINITY, NAN, INFINITY); TEST_EE(casin, NAN, -INFINITY, NAN, -INFINITY); @@ -589,16 +801,16 @@ int test_casin() TEST_BRANCH_CUT(casin, 0, -2, 1, 0, 1, 1, 1); TEST_BRANCH_CUT(casin, 0, 2, 1, 0, 1, 1, 1); - TEST_CC(casin, 0.5, 0, ADDSUFFIX(asin)(0.5), 0); + TEST_CC(casin, 0.5, 0, FUNC(asin)(0.5), 0); return ret; } #endif #ifdef CATAN -int test_catan() +RETTYPE ADDSUFFIX(test_catan)(void) { - int ret = 1; + INIT_FUNC(); /* catan(conj(z)) = conj(catan(z)) and catan is odd */ TEST_EE(catan, 0, 0, 0, 0); TEST_EE(catan, 0, NZERO, 0, NZERO); @@ -613,24 +825,24 @@ int test_catan() TEST_RAISES(catan, NZERO, -1, NZERO, -INFINITY, FE_DIVBYZERO); TEST_RAISES(catan, 0, -1, 0, -INFINITY, FE_DIVBYZERO); - TEST_CE(catan, -INFINITY, 2.0, -NPY_PI_2, 0); - TEST_CE(catan, INFINITY, 2.0, NPY_PI_2, 0); - TEST_CE(catan, -INFINITY, -2.0, -NPY_PI_2, NZERO); - TEST_CE(catan, INFINITY, -2.0, NPY_PI_2, NZERO); + TEST_CE(catan, -INFINITY, 2.0, -ADDSUFFIX(NPY_PI_2), 0); + TEST_CE(catan, INFINITY, 2.0, ADDSUFFIX(NPY_PI_2), 0); + TEST_CE(catan, -INFINITY, -2.0, -ADDSUFFIX(NPY_PI_2), NZERO); + TEST_CE(catan, INFINITY, -2.0, ADDSUFFIX(NPY_PI_2), NZERO); /* can raise FE_INVALID or not */ TEST_EE(catan, NAN, -2.0, NAN, NAN); TEST_EE(catan, NAN, 2.0, NAN, NAN); - TEST_CE(catan, -2.0, INFINITY, -NPY_PI_2, 0); - TEST_CE(catan, 2.0, INFINITY, NPY_PI_2, 0); - TEST_CE(catan, -2.0, -INFINITY, -NPY_PI_2, NZERO); - TEST_CE(catan, 2.0, -INFINITY, NPY_PI_2, NZERO); + TEST_CE(catan, -2.0, INFINITY, -ADDSUFFIX(NPY_PI_2), 0); + TEST_CE(catan, 2.0, INFINITY, ADDSUFFIX(NPY_PI_2), 0); + TEST_CE(catan, -2.0, -INFINITY, -ADDSUFFIX(NPY_PI_2), NZERO); + TEST_CE(catan, 2.0, -INFINITY, ADDSUFFIX(NPY_PI_2), NZERO); - TEST_CE(catan, -INFINITY, INFINITY, -NPY_PI_2, 0); - TEST_CE(catan, INFINITY, INFINITY, NPY_PI_2, 0); - TEST_CE(catan, -INFINITY, -INFINITY, -NPY_PI_2, NZERO); - TEST_CE(catan, INFINITY, -INFINITY, NPY_PI_2, NZERO); + TEST_CE(catan, -INFINITY, INFINITY, -ADDSUFFIX(NPY_PI_2), 0); + TEST_CE(catan, INFINITY, INFINITY, ADDSUFFIX(NPY_PI_2), 0); + TEST_CE(catan, -INFINITY, -INFINITY, -ADDSUFFIX(NPY_PI_2), NZERO); + TEST_CE(catan, INFINITY, -INFINITY, ADDSUFFIX(NPY_PI_2), NZERO); TEST_EE(catan, NAN, INFINITY, NAN, 0); TEST_EE(catan, NAN, -INFINITY, NAN, NZERO); @@ -640,8 +852,8 @@ int test_catan() TEST_EE(catan, 2.0, NAN, NAN, NAN); /* sign of real part is unspecified */ - TEST_UNSPECIFIED2_CE(catan, -INFINITY, NAN, -NPY_PI_2, 0, -NPY_PI_2, NZERO); - TEST_UNSPECIFIED2_CE(catan, INFINITY, NAN, NPY_PI_2, 0, NPY_PI_2, NZERO); + TEST_UNSPECIFIED2_CE(catan, -INFINITY, NAN, -ADDSUFFIX(NPY_PI_2), 0, -ADDSUFFIX(NPY_PI_2), NZERO); + TEST_UNSPECIFIED2_CE(catan, INFINITY, NAN, ADDSUFFIX(NPY_PI_2), 0, ADDSUFFIX(NPY_PI_2), NZERO); TEST_EE(catan, NAN, NAN, NAN, NAN); @@ -654,40 +866,40 @@ int test_catan() TEST_BRANCH_CUT(catan, -2, 0, 0, 1, 1, 1, 1); TEST_BRANCH_CUT(catan, 2, 0, 0, 1, 1, 1, 1); - TEST_CC(catan, 0.5, 0, ADDSUFFIX(catan)(0.5), 0); + TEST_CC(catan, 0.5, 0, FUNC(atan)(0.5), 0); return ret; } #endif #ifdef CACOSH -int test_cacosh() +RETTYPE ADDSUFFIX(test_cacosh)(void) { - int ret = 1; + INIT_FUNC(); /* cacosh(conj(z)) = conj(cacosh(z)) */ - TEST_EC(cacosh, 0, 0, 0, NPY_PI_2); - TEST_EC(cacosh, 0, NZERO, 0, -NPY_PI_2); + TEST_EC(cacosh, 0, 0, 0, ADDSUFFIX(NPY_PI_2)); + TEST_EC(cacosh, 0, NZERO, 0, -ADDSUFFIX(NPY_PI_2)); - TEST_EC(cacosh, NZERO, 0, 0, NPY_PI_2); - TEST_EC(cacosh, NZERO, NZERO, 0, -NPY_PI_2); + TEST_EC(cacosh, NZERO, 0, 0, ADDSUFFIX(NPY_PI_2)); + TEST_EC(cacosh, NZERO, NZERO, 0, -ADDSUFFIX(NPY_PI_2)); - TEST_EC(cacosh, 2.0, INFINITY, INFINITY, NPY_PI_2); - TEST_EC(cacosh, 2.0, -INFINITY, INFINITY, -NPY_PI_2); + TEST_EC(cacosh, 2.0, INFINITY, INFINITY, ADDSUFFIX(NPY_PI_2)); + TEST_EC(cacosh, 2.0, -INFINITY, INFINITY, -ADDSUFFIX(NPY_PI_2)); /* can raise FE_INVALID or not */ TEST_EE(cacosh, 2.0, NAN, NAN, NAN); - TEST_EC(cacosh, -INFINITY, 2.0, INFINITY, NPY_PI); - TEST_EC(cacosh, -INFINITY, -2.0, INFINITY, -NPY_PI); + TEST_EC(cacosh, -INFINITY, 2.0, INFINITY, ADDSUFFIX(NPY_PI)); + TEST_EC(cacosh, -INFINITY, -2.0, INFINITY, -ADDSUFFIX(NPY_PI)); TEST_EE(cacosh, INFINITY, 2.0, INFINITY, 0); TEST_EE(cacosh, INFINITY, -2.0, INFINITY, NZERO); - TEST_EC(cacosh, -INFINITY, INFINITY, INFINITY, 0.75*NPY_PI); - TEST_EC(cacosh, -INFINITY, -INFINITY, INFINITY, -0.75*NPY_PI); + TEST_EC(cacosh, -INFINITY, INFINITY, INFINITY, 0.75*ADDSUFFIX(NPY_PI)); + TEST_EC(cacosh, -INFINITY, -INFINITY, INFINITY, -0.75*ADDSUFFIX(NPY_PI)); - TEST_EC(cacosh, INFINITY, INFINITY, INFINITY, 0.25*NPY_PI); - TEST_EC(cacosh, INFINITY, -INFINITY, INFINITY, -0.25*NPY_PI); + TEST_EC(cacosh, INFINITY, INFINITY, INFINITY, 0.25*ADDSUFFIX(NPY_PI)); + TEST_EC(cacosh, INFINITY, -INFINITY, INFINITY, -0.25*ADDSUFFIX(NPY_PI)); TEST_EE(cacosh, INFINITY, NAN, INFINITY, NAN); TEST_EE(cacosh, -INFINITY, NAN, INFINITY, NAN); @@ -707,25 +919,25 @@ int test_cacosh() TEST_BRANCH_CUT(cacosh, 0, 2, 1, 0, 1, 1, 1); TEST_BRANCH_CUT(cacosh, 2, 0, 0, 1, 1, 1, 1); - TEST_CC(cacosh, 1.5, 0, ADDSUFFIX(acosh)(1.5), 0); + TEST_CC(cacosh, 1.5, 0, FUNC(acosh)(1.5), 0); return ret; } #endif #ifdef CASINH -int test_casinh() +RETTYPE ADDSUFFIX(test_casinh)(void) { - int ret = 1; + INIT_FUNC(); /* casinh(conj(z)) = conj(casinh(z)) and casinh is odd */ TEST_EE(casinh, 0, 0, 0, 0); TEST_EE(casinh, 0, NZERO, 0, NZERO); TEST_EE(casinh, NZERO, 0, NZERO, 0); TEST_EE(casinh, NZERO, NZERO, NZERO, NZERO); - TEST_EC(casinh, 2.0, INFINITY, INFINITY, NPY_PI_2); - TEST_EC(casinh, 2.0, -INFINITY, INFINITY, -NPY_PI_2); - TEST_EC(casinh, -2.0, INFINITY, -INFINITY, NPY_PI_2); - TEST_EC(casinh, -2.0, -INFINITY, -INFINITY, -NPY_PI_2); + TEST_EC(casinh, 2.0, INFINITY, INFINITY, ADDSUFFIX(NPY_PI_2)); + TEST_EC(casinh, 2.0, -INFINITY, INFINITY, -ADDSUFFIX(NPY_PI_2)); + TEST_EC(casinh, -2.0, INFINITY, -INFINITY, ADDSUFFIX(NPY_PI_2)); + TEST_EC(casinh, -2.0, -INFINITY, -INFINITY, -ADDSUFFIX(NPY_PI_2)); /* can raise FE_INVALID or not */ TEST_EE(casinh, 2.0, NAN, NAN, NAN); @@ -736,10 +948,10 @@ int test_casinh() TEST_EE(casinh, -INFINITY, 2.0, -INFINITY, 0); TEST_EE(casinh, -INFINITY, -2.0, -INFINITY, NZERO); - TEST_EC(casinh, INFINITY, INFINITY, INFINITY, 0.25*NPY_PI); - TEST_EC(casinh, INFINITY, -INFINITY, INFINITY, -0.25*NPY_PI); - TEST_EC(casinh, -INFINITY, INFINITY, -INFINITY, 0.25*NPY_PI); - TEST_EC(casinh, -INFINITY, -INFINITY, -INFINITY, -0.25*NPY_PI); + TEST_EC(casinh, INFINITY, INFINITY, INFINITY, 0.25*ADDSUFFIX(NPY_PI)); + TEST_EC(casinh, INFINITY, -INFINITY, INFINITY, -0.25*ADDSUFFIX(NPY_PI)); + TEST_EC(casinh, -INFINITY, INFINITY, -INFINITY, 0.25*ADDSUFFIX(NPY_PI)); + TEST_EC(casinh, -INFINITY, -INFINITY, -INFINITY, -0.25*ADDSUFFIX(NPY_PI)); TEST_EE(casinh, INFINITY, NAN, INFINITY, NAN); TEST_EE(casinh, -INFINITY, NAN, -INFINITY, NAN); @@ -767,16 +979,16 @@ int test_casinh() TEST_BRANCH_CUT(casinh, 2, 0, 0, 1, 1, 1, 1); TEST_BRANCH_CUT(casinh, 0, 0, 1, 0, 1, 1, 1); - TEST_CC(casinh, 0.5, 0, ADDSUFFIX(asinh)(0.5), 0); + TEST_CC(casinh, 0.5, 0, FUNC(asinh)(0.5), 0); return ret; } #endif #ifdef CATANH -int test_catanh() +RETTYPE ADDSUFFIX(test_catanh)(void) { - int ret = 1; + INIT_FUNC(); /* catanh(conj(z)) = conj(catanh(z)) and catanh is odd */ TEST_EE(catanh, 0, 0, 0, 0); TEST_EE(catanh, 0, NZERO, 0, NZERO); @@ -791,24 +1003,24 @@ int test_catanh() TEST_RAISES(catanh, -1, 0, -INFINITY, 0, FE_DIVBYZERO); TEST_RAISES(catanh, -1, NZERO, -INFINITY, NZERO, FE_DIVBYZERO); - TEST_EC(catanh, 2.0, INFINITY, 0, NPY_PI_2); - TEST_EC(catanh, 2.0, -INFINITY, 0, -NPY_PI_2); - TEST_EC(catanh, -2.0, INFINITY, NZERO, NPY_PI_2); - TEST_EC(catanh, -2.0, -INFINITY, NZERO, -NPY_PI_2); + TEST_EC(catanh, 2.0, INFINITY, 0, ADDSUFFIX(NPY_PI_2)); + TEST_EC(catanh, 2.0, -INFINITY, 0, -ADDSUFFIX(NPY_PI_2)); + TEST_EC(catanh, -2.0, INFINITY, NZERO, ADDSUFFIX(NPY_PI_2)); + TEST_EC(catanh, -2.0, -INFINITY, NZERO, -ADDSUFFIX(NPY_PI_2)); /* can raise FE_INVALID or not */ TEST_EE(catanh, 2.0, NAN, NAN, NAN); TEST_EE(catanh, -2.0, NAN, NAN, NAN); - TEST_EC(catanh, INFINITY, 2.0, 0, NPY_PI_2); - TEST_EC(catanh, INFINITY, -2.0, 0, -NPY_PI_2); - TEST_EC(catanh, -INFINITY, 2.0, NZERO, NPY_PI_2); - TEST_EC(catanh, -INFINITY, -2.0, NZERO, -NPY_PI_2); + TEST_EC(catanh, INFINITY, 2.0, 0, ADDSUFFIX(NPY_PI_2)); + TEST_EC(catanh, INFINITY, -2.0, 0, -ADDSUFFIX(NPY_PI_2)); + TEST_EC(catanh, -INFINITY, 2.0, NZERO, ADDSUFFIX(NPY_PI_2)); + TEST_EC(catanh, -INFINITY, -2.0, NZERO, -ADDSUFFIX(NPY_PI_2)); - TEST_EC(catanh, INFINITY, INFINITY, 0, NPY_PI_2); - TEST_EC(catanh, INFINITY, -INFINITY, 0, -NPY_PI_2); - TEST_EC(catanh, -INFINITY, INFINITY, NZERO, NPY_PI_2); - TEST_EC(catanh, -INFINITY, -INFINITY, NZERO, -NPY_PI_2); + TEST_EC(catanh, INFINITY, INFINITY, 0, ADDSUFFIX(NPY_PI_2)); + TEST_EC(catanh, INFINITY, -INFINITY, 0, -ADDSUFFIX(NPY_PI_2)); + TEST_EC(catanh, -INFINITY, INFINITY, NZERO, ADDSUFFIX(NPY_PI_2)); + TEST_EC(catanh, -INFINITY, -INFINITY, NZERO, -ADDSUFFIX(NPY_PI_2)); TEST_EE(catanh, INFINITY, NAN, 0, NAN); TEST_EE(catanh, -INFINITY, NAN, NZERO, NAN); @@ -818,8 +1030,8 @@ int test_catanh() TEST_EE(catanh, NAN, -2.0, NAN, NAN); /* sign of real part is unspecified */ - TEST_UNSPECIFIED2_EC(catanh, NAN, INFINITY, 0, NPY_PI_2, NZERO, NPY_PI_2); - TEST_UNSPECIFIED2_EC(catanh, NAN, -INFINITY, 0, -NPY_PI_2, NZERO, -NPY_PI_2); + TEST_UNSPECIFIED2_EC(catanh, NAN, INFINITY, 0, ADDSUFFIX(NPY_PI_2), NZERO, ADDSUFFIX(NPY_PI_2)); + TEST_UNSPECIFIED2_EC(catanh, NAN, -INFINITY, 0, -ADDSUFFIX(NPY_PI_2), NZERO, -ADDSUFFIX(NPY_PI_2)); TEST_EE(catanh, NAN, NAN, NAN, NAN); @@ -833,16 +1045,16 @@ int test_catanh() TEST_BRANCH_CUT(catanh, 0, 2, 1, 0, 1, 1, 1); TEST_BRANCH_CUT(catanh, 0, 0, 0, 1, 1, 1, 1); - TEST_CC(catanh, 0.5, 0, ADDSUFFIX(atanh)(0.5), 0); + TEST_CC(catanh, 0.5, 0, FUNC(atanh)(0.5), 0); return ret; } #endif #ifdef CCOS -int test_ccos() +RETTYPE ADDSUFFIX(test_ccos)(void) { - int ret = 1; + INIT_FUNC(); /* ccos(conj(z)) = conj(ccos(z)) and ccos is even */ TEST_EE(ccos, NZERO, 0, 1, 0); TEST_EE(ccos, 0, 0, 1, NZERO); @@ -905,16 +1117,16 @@ int test_ccos() TEST_EE(ccos, NAN, NAN, NAN, NAN); - TEST_CC(ccos, 0.5, 0, ADDSUFFIX(cos)(0.5), 0); - + TEST_CC(ccos, 0.5, 0, FUNC(cos)(0.5), 0); + return ret; } #endif #ifdef CSIN -int test_csin() +RETTYPE ADDSUFFIX(test_csin)(void) { - int ret = 1; + INIT_FUNC(); /* csin(conj(z)) = conj(csin(z)) and csin is odd */ TEST_EE(csin, 0, 0, 0, 0); TEST_EE(csin, 0, NZERO, 0, NZERO); @@ -977,16 +1189,16 @@ int test_csin() TEST_EE(csin, NAN, NAN, NAN, NAN); - TEST_CC(csin, 0.5, 0, ADDSUFFIX(sin)(0.5), 0); + TEST_CC(csin, 0.5, 0, FUNC(sin)(0.5), 0); return ret; } #endif #ifdef CTAN -int test_ctan() +RETTYPE ADDSUFFIX(test_ctan)(void) { - int ret = 1; + INIT_FUNC(); /* ctan(conj(z)) = conj(ctan(z)) and ctan is odd */ TEST_EE(ctan, 0, 0, 0, 0); TEST_EE(ctan, 0, NZERO, 0, NZERO); @@ -1028,7 +1240,7 @@ int test_ctan() TEST_EE(ctan, NAN, NAN, NAN, NAN); - TEST_CC(ctan, 0.5, 0, ADDSUFFIX(tan)(0.5), 0); + TEST_CC(ctan, 0.5, 0, FUNC(tan)(0.5), 0); TEST_CC(ctan, 0, 1000, 0, 1); TEST_CC(ctan, 0, -1000, 0, -1); @@ -1038,9 +1250,9 @@ int test_ctan() #endif #ifdef CCOSH -int test_ccosh() +RETTYPE ADDSUFFIX(test_ccosh)(void) { - int ret = 1; + INIT_FUNC(); /* ccosh(conj(z)) = conj(ccosh(z)) and ccosh is even */ TEST_EE(ccosh, 0, 0, 1, 0); TEST_EE(ccosh, 0, NZERO, 1, NZERO); @@ -1103,16 +1315,16 @@ int test_ccosh() TEST_EE(ccosh, NAN, NAN, NAN, NAN); - TEST_CC(ccosh, 0.5, 0, ADDSUFFIX(cosh)(0.5), 0); + TEST_CC(ccosh, 0.5, 0, FUNC(cosh)(0.5), 0); return ret; } #endif #ifdef CSINH -int test_csinh() +RETTYPE ADDSUFFIX(test_csinh)(void) { - int ret = 1; + INIT_FUNC(); /* csinh(conj(z)) = conj(csinh(z)) and csinh is odd */ TEST_EE(csinh, 0, 0, 0, 0); TEST_EE(csinh, 0, NZERO, 0, NZERO); @@ -1175,16 +1387,16 @@ int test_csinh() TEST_EE(csinh, NAN, NAN, NAN, NAN); - TEST_CC(csinh, 0.5, 0, ADDSUFFIX(sinh)(0.5), 0); + TEST_CC(csinh, 0.5, 0, FUNC(sinh)(0.5), 0); return ret; } #endif #ifdef CTANH -int test_ctanh() +RETTYPE ADDSUFFIX(test_ctanh)(void) { - int ret = 1; + INIT_FUNC(); /* ctanh(conj(z)) = conj(ctanh(z)) and ctanh is odd */ TEST_EE(ctanh, 0, 0, 0, 0); TEST_EE(ctanh, 0, NZERO, 0, NZERO); @@ -1226,7 +1438,7 @@ int test_ctanh() TEST_EE(ctanh, NAN, NAN, NAN, NAN); - TEST_CC(ctanh, 0.5, 0, ADDSUFFIX(tanh)(0.5), 0); + TEST_CC(ctanh, 0.5, 0, FUNC(tanh)(0.5), 0); TEST_CC(ctanh, 1000, 0, 1, 0); TEST_CC(ctanh, -1000, 0, -1, 0); @@ -1236,9 +1448,9 @@ int test_ctanh() #endif #ifdef CEXP -int test_cexp() +RETTYPE ADDSUFFIX(test_cexp)(void) { - int ret = 1; + INIT_FUNC(); /* cexp(conj(z)) = conj(cexp(z)) */ TEST_EE(cexp, 0, 0, 1, 0); TEST_EE(cexp, 0, NZERO, 1, NZERO); @@ -1301,42 +1513,42 @@ int test_cexp() TEST_CC(cexp, 0.5, 0, ADDSUFFIX(exp)(0.5), 0); - TEST_CC(cexp, 1, 0, M_E, 0); - TEST_CC(cexp, 0, 1, ADDSUFFIX(cos)(1), ADDSUFFIX(sin)(1)); - TEST_CC(cexp, 1, 1, M_E*ADDSUFFIX(cos)(1), M_E*ADDSUFFIX(sin)(1)); + TEST_CC(cexp, 1, 0, ADDSUFFIX(NPY_E), 0); + TEST_CC(cexp, 0, 1, FUNC(cos)(1), FUNC(sin)(1)); + TEST_CC(cexp, 1, 1, ADDSUFFIX(NPY_E)*FUNC(cos)(1), ADDSUFFIX(NPY_E)*FUNC(sin)(1)); return ret; } #endif #ifdef CLOG -int test_clog() +RETTYPE ADDSUFFIX(test_clog)(void) { - int ret = 1; + INIT_FUNC(); /* clog(conj(z)) = conj(clog(z)) */ - TEST_RAISES(clog, NZERO, 0, -INFINITY, NPY_PI, FE_DIVBYZERO); - TEST_RAISES(clog, NZERO, NZERO, -INFINITY, -NPY_PI, FE_DIVBYZERO); + TEST_RAISES(clog, NZERO, 0, -INFINITY, ADDSUFFIX(NPY_PI), FE_DIVBYZERO); + TEST_RAISES(clog, NZERO, NZERO, -INFINITY, -ADDSUFFIX(NPY_PI), FE_DIVBYZERO); TEST_RAISES(clog, 0, 0, -INFINITY, 0, FE_DIVBYZERO); TEST_RAISES(clog, 0, NZERO, -INFINITY, NZERO, FE_DIVBYZERO); - TEST_EC(clog, 2.0, INFINITY, INFINITY, NPY_PI_2); - TEST_EC(clog, 2.0, -INFINITY, INFINITY, -NPY_PI_2); + TEST_EC(clog, 2.0, INFINITY, INFINITY, ADDSUFFIX(NPY_PI_2)); + TEST_EC(clog, 2.0, -INFINITY, INFINITY, -ADDSUFFIX(NPY_PI_2)); /* can raise FE_INVALID or not */ TEST_EE(clog, 2.0, NAN, NAN, NAN); - TEST_EC(clog, -INFINITY, 2.0, INFINITY, NPY_PI); - TEST_EC(clog, -INFINITY, -2.0, INFINITY, -NPY_PI); + TEST_EC(clog, -INFINITY, 2.0, INFINITY, ADDSUFFIX(NPY_PI)); + TEST_EC(clog, -INFINITY, -2.0, INFINITY, -ADDSUFFIX(NPY_PI)); TEST_EE(clog, INFINITY, 2.0, INFINITY, 0); TEST_EE(clog, INFINITY, -2.0, INFINITY, NZERO); - TEST_EC(clog, -INFINITY, INFINITY, INFINITY, 0.75 * NPY_PI); - TEST_EC(clog, -INFINITY, -INFINITY, INFINITY, -0.75 * NPY_PI); + TEST_EC(clog, -INFINITY, INFINITY, INFINITY, 0.75 * ADDSUFFIX(NPY_PI)); + TEST_EC(clog, -INFINITY, -INFINITY, INFINITY, -0.75 * ADDSUFFIX(NPY_PI)); - TEST_EC(clog, INFINITY, INFINITY, INFINITY, 0.25 * NPY_PI); - TEST_EC(clog, INFINITY, -INFINITY, INFINITY, -0.25 * NPY_PI); + TEST_EC(clog, INFINITY, INFINITY, INFINITY, 0.25 * ADDSUFFIX(NPY_PI)); + TEST_EC(clog, INFINITY, -INFINITY, INFINITY, -0.25 * ADDSUFFIX(NPY_PI)); TEST_EE(clog, INFINITY, NAN, INFINITY, NAN); TEST_EE(clog, -INFINITY, NAN, INFINITY, NAN); @@ -1352,19 +1564,19 @@ int test_clog() TEST_BRANCH_CUT(clog, -0.5, 0, 0, 1, 1, -1, 1); - TEST_CC(clog, 0.5, 0, ADDSUFFIX(log)(0.5), 0); + TEST_CC(clog, 0.5, 0, FUNC(log)(0.5), 0); TEST_CC(clog, 1, 0, 0, 0); - TEST_CC(clog, 1, 2, 0.80471895621705014, 1.1071487177940904); + TEST_CC(clog, 1, 2, 0.80471895621705014, 1.1071487177940904); return ret; } #endif #ifdef CPOW -int test_cpow() +RETTYPE ADDSUFFIX(test_cpow)(void) { - int ret = 1; + INIT_FUNC(); /* there are _no_ annex G values for cpow. */ /* We can check for branch cuts in here */ @@ -1457,13 +1669,13 @@ int test_cpow() TEST_CPOW_CC(1, 0, 3, 0, 1, 0); TEST_CPOW_CC(0, 1, 1, 0, 0, 1); - TEST_CPOW_CC(0, 1, 0, 1, ADDSUFFIX(exp)(-NPY_PI_2), 0); + TEST_CPOW_CC(0, 1, 0, 1, FUNC(exp)(-ADDSUFFIX(NPY_PI_2)), 0); TEST_CPOW_CC(0, 1, -0.5, 1.5, 0.067019739708273365, -0.067019739708273365); TEST_CPOW_CC(0, 1, 2, 0, -1, 0); TEST_CPOW_CC(0, 1, 3, 0, 0, -1); TEST_CPOW_CC(2, 0, 1, 0, 2, 0); - TEST_CPOW_CC(2, 0, 0, 1, ADDSUFFIX(cos)(NPY_LOGE2), ADDSUFFIX(sin)(NPY_LOGE2)); + TEST_CPOW_CC(2, 0, 0, 1, FUNC(cos)(NPY_LOGE2), FUNC(sin)(NPY_LOGE2)); TEST_CPOW_CC(2, 0, 2, 0, 4, 0); TEST_CPOW_CC(2, 0, 3, 0, 8, 0); @@ -1490,9 +1702,9 @@ int test_cpow() #endif #ifdef CSQRT -int test_csqrt() +RETTYPE ADDSUFFIX(test_csqrt)(void) { - int ret = 1; + INIT_FUNC(); /* csqrt(conj(z)) = conj(csqrt(z)) */ TEST_EE(csqrt, 0, 0, 0, 0); TEST_EE(csqrt, 0, NZERO, 0, NZERO); @@ -1534,10 +1746,10 @@ int test_csqrt() TEST_BRANCH_CUT(csqrt, -0.5, 0, 0, 1, 1, -1, 1); - TEST_CC(csqrt, 0.5, 0, ADDSUFFIX(sqrt)(0.5), 0); + TEST_CC(csqrt, 0.5, 0, FUNC(sqrt)(0.5), 0); TEST_CC(csqrt, 1, 0, 1, 0); - TEST_CC(csqrt, 0, 1, NPY_SQRT2/2.0, NPY_SQRT2/2.0); + TEST_CC(csqrt, 0, 1, ADDSUFFIX(NPY_SQRT2)/2.0, ADDSUFFIX(NPY_SQRT2)/2.0); TEST_CC(csqrt, -1, 0, 0, 1); TEST_CC(csqrt, 1, 1, 1.0986841134678100, 0.4550898605622273); TEST_CC(csqrt, 1, -1, 1.0986841134678100, -0.4550898605622273); @@ -1546,55 +1758,67 @@ int test_csqrt() } #endif +#ifndef HAVE_NUMPY int main(int argc, char** argv) { #ifdef CACOS - return !test_cacos(); + return !ADDSUFFIX(test_cacos)(); #endif #ifdef CASIN - return !test_casin(); + return !ADDSUFFIX(test_casin)(); #endif #ifdef CATAN - return !test_catan(); + return !ADDSUFFIX(test_catan)(); #endif #ifdef CACOSH - return !test_cacosh(); + return !ADDSUFFIX(test_cacosh)(); #endif #ifdef CASINH - return !test_casinh(); + return !ADDSUFFIX(test_casinh)(); #endif #ifdef CATANH - return !test_catanh(); + return !ADDSUFFIX(test_catanh)(); #endif #ifdef CCOS - return !test_ccos(); + return !ADDSUFFIX(test_ccos)(); #endif #ifdef CSIN - return !test_csin(); + return !ADDSUFFIX(test_csin)(); #endif #ifdef CTAN - return !test_ctan(); + return !ADDSUFFIX(test_ctan)(); #endif #ifdef CCOSH - return !test_ccosh(); + return !ADDSUFFIX(test_ccosh)(); #endif #ifdef CSINH - return !test_csinh(); + return !ADDSUFFIX(test_csinh)(); #endif #ifdef CTANH - return !test_ctanh(); + return !ADDSUFFIX(test_ctanh)(); #endif #ifdef CEXP - return !test_cexp(); + return !ADDSUFFIX(test_cexp)(); #endif #ifdef CLOG - return !test_clog(); + return !ADDSUFFIX(test_clog)(); #endif #ifdef CPOW - return !test_cpow(); + return !ADDSUFFIX(test_cpow)(); #endif #ifdef CSQRT - return !test_csqrt(); + return !ADDSUFFIX(test_csqrt)(); #endif } +#endif + +#undef TYPE +#undef CTYPE +#undef SUFFIX +#undef EPS +#undef CLOSE_ATOL +#undef CLOSE_RTOL +#undef BRANCH_SCALE +#undef BRANCH_ATOL +#undef FMT diff --git a/numpy/core/tests/test_umath_complex.py b/numpy/core/tests/test_umath_complex.py index 620134f12510..a83ea902c7e0 100644 --- a/numpy/core/tests/test_umath_complex.py +++ b/numpy/core/tests/test_umath_complex.py @@ -1,4 +1,5 @@ from __future__ import division, absolute_import, print_function +from numpy.core.npymath_tests import * import sys import platform @@ -7,6 +8,8 @@ import numpy.core.umath as ncu import numpy as np +import numpy.core.npymath_tests as ncn + # TODO: branch cuts (use Pauli code) # TODO: conj 'symmetry' # TODO: FPU exceptions @@ -532,6 +535,151 @@ def test_simple(self): check_complex_value(ncu.tanh, 1000, 0, 1, 0, True) check_complex_value(ncu.tanh, -1000, 0, -1, 0, True) +class TestNPyMath(object): + def test_cacosf(self): + ncn.test_cacosf() + + def test_cacos(self): + ncn.test_cacos() + + def test_cacosl(self): + ncn.test_cacosl() + + def test_casinf(self): + ncn.test_casinf() + + def test_casin(self): + ncn.test_casin() + + def test_casinl(self): + ncn.test_casinl() + + def test_catanf(self): + ncn.test_catanf() + + def test_catan(self): + ncn.test_catan() + + def test_catanl(self): + ncn.test_catanl() + + def test_cacoshf(self): + ncn.test_cacoshf() + + def test_cacosh(self): + ncn.test_cacosh() + + def test_cacoshl(self): + ncn.test_cacoshl() + + def test_casinhf(self): + ncn.test_casinhf() + + def test_casinh(self): + ncn.test_casinh() + + def test_casinhl(self): + ncn.test_casinhl() + + def test_catanhf(self): + ncn.test_catanhf() + + def test_catanh(self): + ncn.test_catanh() + + def test_catanhl(self): + ncn.test_catanhl() + + def test_ccosf(self): + ncn.test_ccosf() + + def test_ccos(self): + ncn.test_ccos() + + def test_ccosl(self): + ncn.test_ccosl() + + def test_csinf(self): + ncn.test_csinf() + + def test_csin(self): + ncn.test_csin() + + def test_csinl(self): + ncn.test_csinl() + + def test_ctanf(self): + ncn.test_ctanf() + + def test_ctan(self): + ncn.test_ctan() + + def test_ctanl(self): + ncn.test_ctanl() + + def test_ccoshf(self): + ncn.test_ccoshf() + + def test_ccosh(self): + ncn.test_ccosh() + + def test_ccoshl(self): + ncn.test_ccoshl() + + def test_csinhf(self): + ncn.test_csinhf() + + def test_csinh(self): + ncn.test_csinh() + + def test_csinhl(self): + ncn.test_csinhl() + + def test_ctanhf(self): + ncn.test_ctanhf() + + def test_catanh(self): + ncn.test_ctanh() + + def test_ctanhl(self): + ncn.test_ctanhl() + + def test_cexpf(self): + ncn.test_cexpf() + + def test_cexp(self): + ncn.test_cexp() + + def test_cexpl(self): + ncn.test_cexpl() + + def test_clogf(self): + ncn.test_clogf() + + def test_clog(self): + ncn.test_clog() + + def test_clogl(self): + ncn.test_clogl() + + def test_cpowf(self): + ncn.test_cpowf() + + def test_cpow(self): + ncn.test_cpow() + + def test_cpowl(self): + ncn.test_cpowl() + + def test_csqrtf(self): + ncn.test_csqrtf() + + def test_csqrt(self): + ncn.test_csqrt() + + def test_csqrtl(self): + ncn.test_csqrtl() + def check_real_value(f, x1, y1, x, exact=True): z1 = np.array([complex(x1, y1)]) if exact: From cfe9fccf69e9ef8f65a06fce506f7f79e365f191 Mon Sep 17 00:00:00 2001 From: Eric Moore Date: Mon, 21 Oct 2013 19:24:26 -0400 Subject: [PATCH 29/35] BUG: Fix tests to compile under MSVC --- numpy/core/test_c99complex.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/numpy/core/test_c99complex.c b/numpy/core/test_c99complex.c index c1d6740252ba..852d2cb9fcae 100644 --- a/numpy/core/test_c99complex.c +++ b/numpy/core/test_c99complex.c @@ -99,11 +99,16 @@ #define RETTYPE PyObject* +/* MSVC #defines copysign as _copysign. This conflicts with our prefixing */ +#ifdef _MSC_VER +#undef copysign +#endif + #define INIT_FUNC() \ PyObject *ret = PyList_New(0); \ PyObject *entry; \ const size_t bsize = 4096; \ - char buf[bsize]; \ + char buf[4096]; \ int used_size #define TEST_FAILED_INT(printexpr) \ From cbc97faaaaa45cd421c50769ae6acd95e11eb513 Mon Sep 17 00:00:00 2001 From: Eric Moore Date: Mon, 21 Oct 2013 19:56:38 -0400 Subject: [PATCH 30/35] MAINT: pick sunmath.h from npy_math.h --- numpy/core/test_c99complex.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/numpy/core/test_c99complex.c b/numpy/core/test_c99complex.c index 852d2cb9fcae..84944180673f 100644 --- a/numpy/core/test_c99complex.c +++ b/numpy/core/test_c99complex.c @@ -2,6 +2,9 @@ #include #include #include +#ifdef __SUNPRO_CC +#include +#endif #include #define PYTESTPRECISION 1 From 863d97d5e94d8daceb5d806d6727614f2c44a0ff Mon Sep 17 00:00:00 2001 From: Eric Moore Date: Mon, 21 Oct 2013 20:26:46 -0400 Subject: [PATCH 31/35] MAINT: Update note about FreeBSD. The names and copyright dates were updated earlier. --- numpy/core/src/npymath/npy_math_complex.c.src | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/numpy/core/src/npymath/npy_math_complex.c.src b/numpy/core/src/npymath/npy_math_complex.c.src index 68c629b24d82..45e46b363bd0 100644 --- a/numpy/core/src/npymath/npy_math_complex.c.src +++ b/numpy/core/src/npymath/npy_math_complex.c.src @@ -3,8 +3,8 @@ * * Implement some C99-compatible complex math functions * - * Most of the code is taken from the msun library in FreeBSD (HEAD @ 30th June - * 2009), under the following license: + * Most of the code is taken from the msun library in FreeBSD (HEAD @ 4th + * October 2013), under the following license: * * Copyright (c) 2007, 2011 David Schultz * Copyright (c) 2012 Stephen Montgomery-Smith From 23480239df374e1572f51575c4b61e775da850d3 Mon Sep 17 00:00:00 2001 From: Eric Moore Date: Thu, 24 Oct 2013 18:59:22 -0400 Subject: [PATCH 32/35] MAINT: make sure npy_math builds when long double is double double --- numpy/core/src/npymath/npy_math_complex.c.src | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/numpy/core/src/npymath/npy_math_complex.c.src b/numpy/core/src/npymath/npy_math_complex.c.src index 45e46b363bd0..8e890f6ce5bf 100644 --- a/numpy/core/src/npymath/npy_math_complex.c.src +++ b/numpy/core/src/npymath/npy_math_complex.c.src @@ -1444,6 +1444,7 @@ static inline npy_double _real_part_reciprocal(npy_double x, npy_double y) #undef CUTOFF #endif #if @precision@ == 3 +#ifndef HAVE_LDOUBLE_DOUBLE_DOUBLE_BE #define BIAS (LDBL_MAX_EXP - 1) #define CUTOFF (LDBL_MANT_DIG / 2 + 1) static inline npy_longdouble _real_part_reciprocall(npy_longdouble x, @@ -1472,6 +1473,13 @@ static inline npy_longdouble _real_part_reciprocall(npy_longdouble x, } #undef BIAS #undef CUTOFF +#else +static inline npy_longdouble _real_part_reciprocall(npy_longdouble x, + npy_longdouble y) +{ + return x/(x*x + y*y); +} +#endif #endif @ctype@ npy_catanh@c@(@ctype@ z) From 5771daec9838b406fe12777a753a1c6cd1bef27f Mon Sep 17 00:00:00 2001 From: Eric Moore Date: Wed, 6 Nov 2013 22:22:05 -0500 Subject: [PATCH 33/35] MAINT: move npy_(get,clear)_floatstatus() to their own file. --- numpy/core/setup.py | 3 +- numpy/core/src/npymath/fpstatus.c | 257 +++++++++++++++++++++++++++ numpy/core/src/npymath/ieee754.c.src | 255 -------------------------- 3 files changed, 259 insertions(+), 256 deletions(-) create mode 100644 numpy/core/src/npymath/fpstatus.c diff --git a/numpy/core/setup.py b/numpy/core/setup.py index b9c5db13c21a..d6a3f83a4f6d 100644 --- a/numpy/core/setup.py +++ b/numpy/core/setup.py @@ -712,7 +712,8 @@ def get_mathlib_info(*args): npymath_sources = [join('src', 'npymath', 'npy_math.c.src'), join('src', 'npymath', 'ieee754.c.src'), join('src', 'npymath', 'npy_math_complex.c.src'), - join('src', 'npymath', 'halffloat.c')] + join('src', 'npymath', 'halffloat.c'), + join('src', 'npymath', 'fpstatus.c')] config.add_installed_library('npymath', sources=npymath_sources + [get_mathlib_info], install_dir='lib') diff --git a/numpy/core/src/npymath/fpstatus.c b/numpy/core/src/npymath/fpstatus.c new file mode 100644 index 000000000000..9325fc42ca8e --- /dev/null +++ b/numpy/core/src/npymath/fpstatus.c @@ -0,0 +1,257 @@ +/* + * Functions to set the floating point status word. + * keep in sync with NO_FLOATING_POINT_SUPPORT in ufuncobject.h + */ + +#include "npy_math_common.h" + +#if (defined(__unix__) || defined(unix)) && !defined(USG) +#include +#endif + +/* Solaris --------------------------------------------------------*/ +/* --------ignoring SunOS ieee_flags approach, someone else can +** deal with that! */ +#if defined(sun) || defined(__BSD__) || defined(__OpenBSD__) || \ + (defined(__FreeBSD__) && (__FreeBSD_version < 502114)) || \ + defined(__NetBSD__) +#include + +int npy_get_floatstatus(void) +{ + int fpstatus = fpgetsticky(); + return ((FP_X_DZ & fpstatus) ? NPY_FPE_DIVIDEBYZERO : 0) | + ((FP_X_OFL & fpstatus) ? NPY_FPE_OVERFLOW : 0) | + ((FP_X_UFL & fpstatus) ? NPY_FPE_UNDERFLOW : 0) | + ((FP_X_INV & fpstatus) ? NPY_FPE_INVALID : 0); +} + +int npy_clear_floatstatus(void) +{ + int fpstatus = npy_get_floatstatus(); + fpsetsticky(0); + + return fpstatus; +} + +void npy_set_floatstatus_divbyzero(void) +{ + fpsetsticky(FP_X_DZ); +} + +void npy_set_floatstatus_overflow(void) +{ + fpsetsticky(FP_X_OFL); +} + +void npy_set_floatstatus_underflow(void) +{ + fpsetsticky(FP_X_UFL); +} + +void npy_set_floatstatus_invalid(void) +{ + fpsetsticky(FP_X_INV); +} + + +#elif defined(__GLIBC__) || defined(__APPLE__) || \ + defined(__CYGWIN__) || defined(__MINGW32__) || \ + (defined(__FreeBSD__) && (__FreeBSD_version >= 502114)) + +# if defined(__GLIBC__) || defined(__APPLE__) || \ + defined(__MINGW32__) || defined(__FreeBSD__) +# include +# elif defined(__CYGWIN__) +# include "numpy/fenv/fenv.h" +# endif + +int npy_get_floatstatus(void) +{ + int fpstatus = fetestexcept(FE_DIVBYZERO | FE_OVERFLOW | + FE_UNDERFLOW | FE_INVALID); + + return ((FE_DIVBYZERO & fpstatus) ? NPY_FPE_DIVIDEBYZERO : 0) | + ((FE_OVERFLOW & fpstatus) ? NPY_FPE_OVERFLOW : 0) | + ((FE_UNDERFLOW & fpstatus) ? NPY_FPE_UNDERFLOW : 0) | + ((FE_INVALID & fpstatus) ? NPY_FPE_INVALID : 0); +} + +int npy_clear_floatstatus(void) +{ + /* testing float status is 50-100 times faster than clearing on x86 */ + int fpstatus = npy_get_floatstatus(); + if (fpstatus != 0) { + feclearexcept(FE_DIVBYZERO | FE_OVERFLOW | + FE_UNDERFLOW | FE_INVALID); + } + + return fpstatus; +} + + +void npy_set_floatstatus_divbyzero(void) +{ + feraiseexcept(FE_DIVBYZERO); +} + +void npy_set_floatstatus_overflow(void) +{ + feraiseexcept(FE_OVERFLOW); +} + +void npy_set_floatstatus_underflow(void) +{ + feraiseexcept(FE_UNDERFLOW); +} + +void npy_set_floatstatus_invalid(void) +{ + feraiseexcept(FE_INVALID); +} + +#elif defined(_AIX) +#include +#include + +int npy_get_floatstatus(void) +{ + int fpstatus = fp_read_flag(); + return ((FP_DIV_BY_ZERO & fpstatus) ? NPY_FPE_DIVIDEBYZERO : 0) | + ((FP_OVERFLOW & fpstatus) ? NPY_FPE_OVERFLOW : 0) | + ((FP_UNDERFLOW & fpstatus) ? NPY_FPE_UNDERFLOW : 0) | + ((FP_INVALID & fpstatus) ? NPY_FPE_INVALID : 0); +} + +int npy_clear_floatstatus(void) +{ + int fpstatus = npy_get_floatstatus(); + fp_swap_flag(0); + + return fpstatus; +} + +void npy_set_floatstatus_divbyzero(void) +{ + fp_raise_xcp(FP_DIV_BY_ZERO); +} + +void npy_set_floatstatus_overflow(void) +{ + fp_raise_xcp(FP_OVERFLOW); +} + +void npy_set_floatstatus_underflow(void) +{ + fp_raise_xcp(FP_UNDERFLOW); +} + +void npy_set_floatstatus_invalid(void) +{ + fp_raise_xcp(FP_INVALID); +} + +#else + +/* MS Windows -----------------------------------------------------*/ +#if defined(_MSC_VER) + +#include + + +int npy_get_floatstatus(void) +{ +#if defined(_WIN64) + int fpstatus = _statusfp(); +#else + /* windows enables sse on 32 bit, so check both flags */ + int fpstatus, fpstatus2; + _statusfp2(&fpstatus, &fpstatus2); + fpstatus |= fpstatus2; +#endif + return ((SW_ZERODIVIDE & fpstatus) ? NPY_FPE_DIVIDEBYZERO : 0) | + ((SW_OVERFLOW & fpstatus) ? NPY_FPE_OVERFLOW : 0) | + ((SW_UNDERFLOW & fpstatus) ? NPY_FPE_UNDERFLOW : 0) | + ((SW_INVALID & fpstatus) ? NPY_FPE_INVALID : 0); +} + +int npy_clear_floatstatus(void) +{ + int fpstatus = npy_get_floatstatus(); + _clearfp(); + + return fpstatus; +} + +/* OSF/Alpha (Tru64) ---------------------------------------------*/ +#elif defined(__osf__) && defined(__alpha) + +#include + +int npy_get_floatstatus(void) +{ + unsigned long fpstatus = ieee_get_fp_control(); + return ((IEEE_STATUS_DZE & fpstatus) ? NPY_FPE_DIVIDEBYZERO : 0) | + ((IEEE_STATUS_OVF & fpstatus) ? NPY_FPE_OVERFLOW : 0) | + ((IEEE_STATUS_UNF & fpstatus) ? NPY_FPE_UNDERFLOW : 0) | + ((IEEE_STATUS_INV & fpstatus) ? NPY_FPE_INVALID : 0); +} + +int npy_clear_floatstatus(void) +{ + long fpstatus = npy_get_floatstatus(); + /* clear status bits as well as disable exception mode if on */ + ieee_set_fp_control(0); + + return fpstatus; +} + +#else + +int npy_get_floatstatus(void) +{ + return 0; +} + +int npy_clear_floatstatus(void) +{ + return 0; +} + +#endif + +/* + * By using a volatile floating point value, + * the compiler is forced to actually do the requested + * operations because of potential concurrency. + * + * We shouldn't write multiple values to a single + * global here, because that would cause + * a race condition. + */ +static volatile double _npy_floatstatus_x, + _npy_floatstatus_zero = 0.0, _npy_floatstatus_big = 1e300, + _npy_floatstatus_small = 1e-300, _npy_floatstatus_inf; + +void npy_set_floatstatus_divbyzero(void) +{ + _npy_floatstatus_x = 1.0 / _npy_floatstatus_zero; +} + +void npy_set_floatstatus_overflow(void) +{ + _npy_floatstatus_x = _npy_floatstatus_big * 1e300; +} + +void npy_set_floatstatus_underflow(void) +{ + _npy_floatstatus_x = _npy_floatstatus_small * 1e-300; +} + +void npy_set_floatstatus_invalid(void) +{ + _npy_floatstatus_inf = NPY_INFINITY; + _npy_floatstatus_x = _npy_floatstatus_inf - NPY_INFINITY; +} + +#endif diff --git a/numpy/core/src/npymath/ieee754.c.src b/numpy/core/src/npymath/ieee754.c.src index 6e3426606142..b3157f8e0787 100644 --- a/numpy/core/src/npymath/ieee754.c.src +++ b/numpy/core/src/npymath/ieee754.c.src @@ -556,258 +556,3 @@ npy_longdouble npy_nextafterl(npy_longdouble x, npy_longdouble y) } #endif -/* - * Functions to set the floating point status word. - * keep in sync with NO_FLOATING_POINT_SUPPORT in ufuncobject.h - */ - -#if (defined(__unix__) || defined(unix)) && !defined(USG) -#include -#endif - -/* Solaris --------------------------------------------------------*/ -/* --------ignoring SunOS ieee_flags approach, someone else can -** deal with that! */ -#if defined(sun) || defined(__BSD__) || defined(__OpenBSD__) || \ - (defined(__FreeBSD__) && (__FreeBSD_version < 502114)) || \ - defined(__NetBSD__) -#include - -int npy_get_floatstatus(void) -{ - int fpstatus = fpgetsticky(); - return ((FP_X_DZ & fpstatus) ? NPY_FPE_DIVIDEBYZERO : 0) | - ((FP_X_OFL & fpstatus) ? NPY_FPE_OVERFLOW : 0) | - ((FP_X_UFL & fpstatus) ? NPY_FPE_UNDERFLOW : 0) | - ((FP_X_INV & fpstatus) ? NPY_FPE_INVALID : 0); -} - -int npy_clear_floatstatus(void) -{ - int fpstatus = npy_get_floatstatus(); - fpsetsticky(0); - - return fpstatus; -} - -void npy_set_floatstatus_divbyzero(void) -{ - fpsetsticky(FP_X_DZ); -} - -void npy_set_floatstatus_overflow(void) -{ - fpsetsticky(FP_X_OFL); -} - -void npy_set_floatstatus_underflow(void) -{ - fpsetsticky(FP_X_UFL); -} - -void npy_set_floatstatus_invalid(void) -{ - fpsetsticky(FP_X_INV); -} - - -#elif defined(__GLIBC__) || defined(__APPLE__) || \ - defined(__CYGWIN__) || defined(__MINGW32__) || \ - (defined(__FreeBSD__) && (__FreeBSD_version >= 502114)) - -# if defined(__GLIBC__) || defined(__APPLE__) || \ - defined(__MINGW32__) || defined(__FreeBSD__) -# include -# elif defined(__CYGWIN__) -# include "numpy/fenv/fenv.h" -# endif - -int npy_get_floatstatus(void) -{ - int fpstatus = fetestexcept(FE_DIVBYZERO | FE_OVERFLOW | - FE_UNDERFLOW | FE_INVALID); - - return ((FE_DIVBYZERO & fpstatus) ? NPY_FPE_DIVIDEBYZERO : 0) | - ((FE_OVERFLOW & fpstatus) ? NPY_FPE_OVERFLOW : 0) | - ((FE_UNDERFLOW & fpstatus) ? NPY_FPE_UNDERFLOW : 0) | - ((FE_INVALID & fpstatus) ? NPY_FPE_INVALID : 0); -} - -int npy_clear_floatstatus(void) -{ - /* testing float status is 50-100 times faster than clearing on x86 */ - int fpstatus = npy_get_floatstatus(); - if (fpstatus != 0) { - feclearexcept(FE_DIVBYZERO | FE_OVERFLOW | - FE_UNDERFLOW | FE_INVALID); - } - - return fpstatus; -} - - -void npy_set_floatstatus_divbyzero(void) -{ - feraiseexcept(FE_DIVBYZERO); -} - -void npy_set_floatstatus_overflow(void) -{ - feraiseexcept(FE_OVERFLOW); -} - -void npy_set_floatstatus_underflow(void) -{ - feraiseexcept(FE_UNDERFLOW); -} - -void npy_set_floatstatus_invalid(void) -{ - feraiseexcept(FE_INVALID); -} - -#elif defined(_AIX) -#include -#include - -int npy_get_floatstatus(void) -{ - int fpstatus = fp_read_flag(); - return ((FP_DIV_BY_ZERO & fpstatus) ? NPY_FPE_DIVIDEBYZERO : 0) | - ((FP_OVERFLOW & fpstatus) ? NPY_FPE_OVERFLOW : 0) | - ((FP_UNDERFLOW & fpstatus) ? NPY_FPE_UNDERFLOW : 0) | - ((FP_INVALID & fpstatus) ? NPY_FPE_INVALID : 0); -} - -int npy_clear_floatstatus(void) -{ - int fpstatus = npy_get_floatstatus(); - fp_swap_flag(0); - - return fpstatus; -} - -void npy_set_floatstatus_divbyzero(void) -{ - fp_raise_xcp(FP_DIV_BY_ZERO); -} - -void npy_set_floatstatus_overflow(void) -{ - fp_raise_xcp(FP_OVERFLOW); -} - -void npy_set_floatstatus_underflow(void) -{ - fp_raise_xcp(FP_UNDERFLOW); -} - -void npy_set_floatstatus_invalid(void) -{ - fp_raise_xcp(FP_INVALID); -} - -#else - -/* MS Windows -----------------------------------------------------*/ -#if defined(_MSC_VER) - -#include - - -int npy_get_floatstatus(void) -{ -#if defined(_WIN64) - int fpstatus = _statusfp(); -#else - /* windows enables sse on 32 bit, so check both flags */ - int fpstatus, fpstatus2; - _statusfp2(&fpstatus, &fpstatus2); - fpstatus |= fpstatus2; -#endif - return ((SW_ZERODIVIDE & fpstatus) ? NPY_FPE_DIVIDEBYZERO : 0) | - ((SW_OVERFLOW & fpstatus) ? NPY_FPE_OVERFLOW : 0) | - ((SW_UNDERFLOW & fpstatus) ? NPY_FPE_UNDERFLOW : 0) | - ((SW_INVALID & fpstatus) ? NPY_FPE_INVALID : 0); -} - -int npy_clear_floatstatus(void) -{ - int fpstatus = npy_get_floatstatus(); - _clearfp(); - - return fpstatus; -} - -/* OSF/Alpha (Tru64) ---------------------------------------------*/ -#elif defined(__osf__) && defined(__alpha) - -#include - -int npy_get_floatstatus(void) -{ - unsigned long fpstatus = ieee_get_fp_control(); - return ((IEEE_STATUS_DZE & fpstatus) ? NPY_FPE_DIVIDEBYZERO : 0) | - ((IEEE_STATUS_OVF & fpstatus) ? NPY_FPE_OVERFLOW : 0) | - ((IEEE_STATUS_UNF & fpstatus) ? NPY_FPE_UNDERFLOW : 0) | - ((IEEE_STATUS_INV & fpstatus) ? NPY_FPE_INVALID : 0); -} - -int npy_clear_floatstatus(void) -{ - long fpstatus = npy_get_floatstatus(); - /* clear status bits as well as disable exception mode if on */ - ieee_set_fp_control(0); - - return fpstatus; -} - -#else - -int npy_get_floatstatus(void) -{ - return 0; -} - -int npy_clear_floatstatus(void) -{ - return 0; -} - -#endif - -/* - * By using a volatile floating point value, - * the compiler is forced to actually do the requested - * operations because of potential concurrency. - * - * We shouldn't write multiple values to a single - * global here, because that would cause - * a race condition. - */ -static volatile double _npy_floatstatus_x, - _npy_floatstatus_zero = 0.0, _npy_floatstatus_big = 1e300, - _npy_floatstatus_small = 1e-300, _npy_floatstatus_inf; - -void npy_set_floatstatus_divbyzero(void) -{ - _npy_floatstatus_x = 1.0 / _npy_floatstatus_zero; -} - -void npy_set_floatstatus_overflow(void) -{ - _npy_floatstatus_x = _npy_floatstatus_big * 1e300; -} - -void npy_set_floatstatus_underflow(void) -{ - _npy_floatstatus_x = _npy_floatstatus_small * 1e-300; -} - -void npy_set_floatstatus_invalid(void) -{ - _npy_floatstatus_inf = NPY_INFINITY; - _npy_floatstatus_x = _npy_floatstatus_inf - NPY_INFINITY; -} - -#endif From 566c15ed304a397d7e506d8afd2807c1b8c70370 Mon Sep 17 00:00:00 2001 From: Eric Moore Date: Wed, 6 Nov 2013 23:39:57 -0500 Subject: [PATCH 34/35] BUG Use the floating point status functions in build time tests --- numpy/core/setup.py | 9 +- numpy/core/src/npymath/fpstatus.c | 5 + numpy/core/test_c99complex.c | 150 ++++++++++++++++++++---------- 3 files changed, 110 insertions(+), 54 deletions(-) diff --git a/numpy/core/setup.py b/numpy/core/setup.py index d6a3f83a4f6d..2133859fdcf9 100644 --- a/numpy/core/setup.py +++ b/numpy/core/setup.py @@ -233,13 +233,16 @@ def check_prec(prec): exists.extend(C99_COMPLEX_FUNCS_CHECKED) if len(exists) > 0: - fp = open('./numpy/core/test_c99complex.c', 'r') + fp = open(join('.', 'numpy', 'core', 'test_c99complex.c'), 'r') obody = fp.read() fp.close() precname = {'f':'FLOAT', '':'DOUBLE', 'l':'LONGDOUBLE'}[prec] for f in exists: - body = obody.replace('PYTESTPRECISION', precname).replace('PYTESTFUNC', f.upper()) - if config.try_run(body, libraries=mathlibs): + body = obody.replace('PYTESTPRECISION', precname) \ + .replace('PYTESTFUNC', f.upper()) + inc_dir = join('.', 'numpy', 'core', 'src', 'npymath') + if config.try_run(body, libraries=mathlibs, + include_dirs=[inc_dir]): priv.append((fname2def(f + prec), 1)) check_prec('') diff --git a/numpy/core/src/npymath/fpstatus.c b/numpy/core/src/npymath/fpstatus.c index 9325fc42ca8e..da669ec1b61f 100644 --- a/numpy/core/src/npymath/fpstatus.c +++ b/numpy/core/src/npymath/fpstatus.c @@ -3,7 +3,12 @@ * keep in sync with NO_FLOATING_POINT_SUPPORT in ufuncobject.h */ +/* This include is wrapped so that these functions can also be called when + * doing config tests. + */ +#ifndef CONFIG_TESTS #include "npy_math_common.h" +#endif #if (defined(__unix__) || defined(unix)) && !defined(USG) #include diff --git a/numpy/core/test_c99complex.c b/numpy/core/test_c99complex.c index 84944180673f..64abe107775a 100644 --- a/numpy/core/test_c99complex.c +++ b/numpy/core/test_c99complex.c @@ -1,11 +1,16 @@ -#include -#include +#ifdef HAVE_NUMPY +#include +#include +#else #include #include #ifdef __SUNPRO_CC #include #endif #include +#endif + +#include #define PYTESTPRECISION 1 #define PYTESTFUNC 1 @@ -154,6 +159,18 @@ FMT "j\n", __LINE__, xr, xi, er1, ei1, er2, ei2, er3, ei3, er4, ei4, \ rr, ri)) +#define TEST_RAISES_FAILED(func, xr, xi, er, ei, rr, ri, efpe, rfpe) \ + TEST_FAILED_INT(PyOS_snprintf(buf, bsize, "%d: " STRINGIZE(func) \ + "(" FMT " + " FMT "j): expected: " FMT " + " FMT "j: received: " FMT \ + " + " FMT "j, required FPE: %d, recieved: %d\n", __LINE__, xr, xi, \ + er, ei, rr, ri, efpe, rfpe)) + +#define TEST_RAISES_FAILED2(func, xr, xi, er1, ei1, er2, ei2, rr, ri, efpe, rfpe) \ + TEST_FAILED_INT(PyOS_snprintf(buf, bsize, "%d: " STRINGIZE(func) \ + "(" FMT " + " FMT "j): expected: " FMT " + " FMT "j or " FMT " + " FMT \ + "j: received: " FMT " + " FMT "j, required FPE: %d, recieved: %d\n", \ + __LINE__, xr, xi, er1, ei1, er2, ei2, rr, ri, efpe, rfpe)) + #define TEST_CPOW_FAILED(func, xr, xi, yr, yi, er, ei, rr, ri) \ TEST_FAILED_INT(PyOS_snprintf(buf, bsize, "%d: " STRINGIZE(func) "(" FMT \ " + " FMT "j, " FMT " + " FMT "j): expected: " FMT " + " FMT \ @@ -239,6 +256,18 @@ #define NPY_PI_2l 1.570796326794896619231321691639751442L /* pi/2 */ #define NPY_SQRT2l 1.414213562373095048801688724209698079L /* sqrt(2) */ +#define NPY_FPE_DIVIDEBYZERO 1 +#define NPY_FPE_OVERFLOW 2 +#define NPY_FPE_UNDERFLOW 4 +#define NPY_FPE_INVALID 8 + +/* Including this directly is the easiest way to get npy_clear_floatstatus and + * npy_get_float_status during config tests. Defining CONFIG_TESTS makes this + * independent of numpy. + */ +#define CONFIG_TESTS +#include + #define PREFIX #define RETTYPE int @@ -273,6 +302,25 @@ } \ while(0) +#define TEST_RAISES_FAILED(func, xr, xi, er, ei, rr, ri, efpe, rfpe) \ + do { \ + ret = 0; \ + printf("%d: " STRINGIZE(func) "(" FMT " + " FMT "j): expected: " FMT \ + " + " FMT "j: received: " FMT " + " FMT "j, required FPE: %d, " \ + "recieved: %d\n", __LINE__, xr, xi, er, ei, rr, ri, efpe, rfpe); \ + } \ + while(0) + +#define TEST_RAISES_FAILED2(func, xr, xi, er1, ei1, er2, ei2, rr, ri, efpe, rfpe) \ + do { \ + ret = 0; \ + printf("%d: " STRINGIZE(func) "(" FMT " + " FMT "j): expected: " FMT \ + " + " FMT "j or " FMT " + " FMT "j: received: " FMT " + " FMT \ + "j, required FPE: %d, recieved: %d\n", __LINE__, xr, xi, er1, \ + ei1, er2, ei2, rr, ri, efpe, rfpe); \ + } \ + while(0) + #define TEST_CPOW_FAILED(func, xr, xi, yr, yi, er, ei, rr, ri) \ do { \ ret = 0; \ @@ -440,14 +488,14 @@ CTYPE r; \ CTYPE x = FUNC(cpack)(xr, xi); \ TYPE rr, ri; \ - feclearexcept(FE_ALL_EXCEPT); \ + npy_clear_floatstatus(); \ r = FUNC(func)(x); \ - except = fetestexcept(fpe); \ + except = npy_get_floatstatus(); \ rr = FUNC(creal)(r); \ ri = FUNC(cimag)(r); \ if (!(except & fpe && ADDSUFFIX(isequal)(rr, der) \ && ADDSUFFIX(isequal)(ri, dei))) { \ - TEST_FAILED(FUNC(func), dxr, dxi, der, dei, rr, ri); \ + TEST_RAISES_FAILED(FUNC(func), dxr, dxi, der, dei, rr, ri, fpe, except); \ } \ } \ while(0) @@ -464,15 +512,15 @@ CTYPE r; \ CTYPE x = FUNC(cpack)(xr, xi); \ TYPE rr, ri; \ - feclearexcept(FE_ALL_EXCEPT); \ + npy_clear_floatstatus(); \ r = FUNC(func)(x); \ - except = fetestexcept(fpe); \ + except = npy_get_floatstatus(); \ rr = FUNC(creal)(r); \ ri = FUNC(cimag)(r); \ if (!(except & fpe && \ ((ADDSUFFIX(isequal)(rr, der1) && ADDSUFFIX(isequal)(ri, dei1)) || \ (ADDSUFFIX(isequal)(rr, der2) && ADDSUFFIX(isequal)(ri, dei2))))) {\ - TEST_FAILED2(FUNC(func), dxr, dxi, der1, dei1, der2, dei2, rr, ri);\ + TEST_RAISES_FAILED2(FUNC(func), dxr, dxi, der1, dei1, der2, dei2, rr, ri, fpe, except);\ } \ } \ while(0) @@ -828,10 +876,10 @@ RETTYPE ADDSUFFIX(test_catan)(void) TEST_EE(catan, NAN, 0, NAN, 0); TEST_EE(catan, NAN, NZERO, NAN, NZERO); - TEST_RAISES(catan, NZERO, 1, NZERO, INFINITY, FE_DIVBYZERO); - TEST_RAISES(catan, 0, 1, 0, INFINITY, FE_DIVBYZERO); - TEST_RAISES(catan, NZERO, -1, NZERO, -INFINITY, FE_DIVBYZERO); - TEST_RAISES(catan, 0, -1, 0, -INFINITY, FE_DIVBYZERO); + TEST_RAISES(catan, NZERO, 1, NZERO, INFINITY, NPY_FPE_DIVIDEBYZERO); + TEST_RAISES(catan, 0, 1, 0, INFINITY, NPY_FPE_DIVIDEBYZERO); + TEST_RAISES(catan, NZERO, -1, NZERO, -INFINITY, NPY_FPE_DIVIDEBYZERO); + TEST_RAISES(catan, 0, -1, 0, -INFINITY, NPY_FPE_DIVIDEBYZERO); TEST_CE(catan, -INFINITY, 2.0, -ADDSUFFIX(NPY_PI_2), 0); TEST_CE(catan, INFINITY, 2.0, ADDSUFFIX(NPY_PI_2), 0); @@ -1006,10 +1054,10 @@ RETTYPE ADDSUFFIX(test_catanh)(void) TEST_EE(catanh, 0, NAN, 0, NAN); TEST_EE(catanh, NZERO, NAN, NZERO, NAN); - TEST_RAISES(catanh, 1, 0, INFINITY, 0, FE_DIVBYZERO); - TEST_RAISES(catanh, 1, NZERO, INFINITY, NZERO, FE_DIVBYZERO); - TEST_RAISES(catanh, -1, 0, -INFINITY, 0, FE_DIVBYZERO); - TEST_RAISES(catanh, -1, NZERO, -INFINITY, NZERO, FE_DIVBYZERO); + TEST_RAISES(catanh, 1, 0, INFINITY, 0, NPY_FPE_DIVIDEBYZERO); + TEST_RAISES(catanh, 1, NZERO, INFINITY, NZERO, NPY_FPE_DIVIDEBYZERO); + TEST_RAISES(catanh, -1, 0, -INFINITY, 0, NPY_FPE_DIVIDEBYZERO); + TEST_RAISES(catanh, -1, NZERO, -INFINITY, NZERO, NPY_FPE_DIVIDEBYZERO); TEST_EC(catanh, 2.0, INFINITY, 0, ADDSUFFIX(NPY_PI_2)); TEST_EC(catanh, 2.0, -INFINITY, 0, -ADDSUFFIX(NPY_PI_2)); @@ -1071,16 +1119,16 @@ RETTYPE ADDSUFFIX(test_ccos)(void) /* sign of imaginary part is unspecified */ TEST_RAISES_UNSPECIFIED2(ccos, -INFINITY, 0, NAN, 0, \ - NAN, NZERO, FE_INVALID); + NAN, NZERO, NPY_FPE_INVALID); /* sign of imaginary part is unspecified */ TEST_UNSPECIFIED2(ccos, NAN, 0, NAN, 0, NAN, NZERO); TEST_UNSPECIFIED2(ccos, NAN, NZERO, NAN, 0, NAN, NZERO); - TEST_RAISES(ccos, -INFINITY, 2.0, NAN, NAN, FE_INVALID); - TEST_RAISES(ccos, INFINITY, 2.0, NAN, NAN, FE_INVALID); - TEST_RAISES(ccos, -INFINITY, -2.0, NAN, NAN, FE_INVALID); - TEST_RAISES(ccos, INFINITY, -2.0, NAN, NAN, FE_INVALID); + TEST_RAISES(ccos, -INFINITY, 2.0, NAN, NAN, NPY_FPE_INVALID); + TEST_RAISES(ccos, INFINITY, 2.0, NAN, NAN, NPY_FPE_INVALID); + TEST_RAISES(ccos, -INFINITY, -2.0, NAN, NAN, NPY_FPE_INVALID); + TEST_RAISES(ccos, INFINITY, -2.0, NAN, NAN, NPY_FPE_INVALID); /* can raise FE_INVALID or not */ TEST_EE(ccos, NAN, 2.0, NAN, NAN); @@ -1110,7 +1158,7 @@ RETTYPE ADDSUFFIX(test_ccos)(void) /* sign of real part is unspecified */ TEST_RAISES_UNSPECIFIED2(ccos, -INFINITY, INFINITY, INFINITY, NAN, \ - -INFINITY, NAN, FE_INVALID); + -INFINITY, NAN, NPY_FPE_INVALID); TEST_EE(ccos, NAN, INFINITY, INFINITY, NAN); TEST_EE(ccos, NAN, -INFINITY, INFINITY, NAN); @@ -1143,16 +1191,16 @@ RETTYPE ADDSUFFIX(test_csin)(void) /* sign of imaginary part is unspecified */ TEST_RAISES_UNSPECIFIED2(csin, -INFINITY, 0, NAN, 0, \ - NAN, NZERO, FE_INVALID); + NAN, NZERO, NPY_FPE_INVALID); /* sign of imaginary part is unspecified */ TEST_UNSPECIFIED2(csin, NAN, 0, NAN, 0, NAN, NZERO); TEST_UNSPECIFIED2(csin, NAN, NZERO, NAN, 0, NAN, NZERO); - TEST_RAISES(csin, -INFINITY, 2.0, NAN, NAN, FE_INVALID); - TEST_RAISES(csin, INFINITY, 2.0, NAN, NAN, FE_INVALID); - TEST_RAISES(csin, -INFINITY, -2.0, NAN, NAN, FE_INVALID); - TEST_RAISES(csin, INFINITY, -2.0, NAN, NAN, FE_INVALID); + TEST_RAISES(csin, -INFINITY, 2.0, NAN, NAN, NPY_FPE_INVALID); + TEST_RAISES(csin, INFINITY, 2.0, NAN, NAN, NPY_FPE_INVALID); + TEST_RAISES(csin, -INFINITY, -2.0, NAN, NAN, NPY_FPE_INVALID); + TEST_RAISES(csin, INFINITY, -2.0, NAN, NAN, NPY_FPE_INVALID); /* can raise FE_INVALID or not */ TEST_EE(csin, NAN, 2.0, NAN, NAN); @@ -1182,7 +1230,7 @@ RETTYPE ADDSUFFIX(test_csin)(void) /* sign of imaginary part is unspecified */ TEST_RAISES_UNSPECIFIED2(csin, -INFINITY, INFINITY, NAN, INFINITY, \ - NAN, -INFINITY, FE_INVALID); + NAN, -INFINITY, NPY_FPE_INVALID); /* sign of imaginary part is unspecified */ TEST_UNSPECIFIED2(csin, NAN, INFINITY, NAN, INFINITY, NAN, -INFINITY); @@ -1213,8 +1261,8 @@ RETTYPE ADDSUFFIX(test_ctan)(void) TEST_EE(ctan, NZERO, 0, NZERO, 0); TEST_EE(ctan, NZERO, NZERO, NZERO, NZERO); - TEST_RAISES(ctan, -INFINITY, 2.0, NAN, NAN, FE_INVALID); - TEST_RAISES(ctan, -INFINITY, -2.0, NAN, NAN, FE_INVALID); + TEST_RAISES(ctan, -INFINITY, 2.0, NAN, NAN, NPY_FPE_INVALID); + TEST_RAISES(ctan, -INFINITY, -2.0, NAN, NAN, NPY_FPE_INVALID); /* can raise FE_INVALID or not */ TEST_EE(ctan, NAN, 2.0, NAN, NAN); @@ -1269,16 +1317,16 @@ RETTYPE ADDSUFFIX(test_ccosh)(void) /* sign of imaginary part is unspecified */ TEST_RAISES_UNSPECIFIED2(ccosh, 0, INFINITY, NAN, 0, \ - NAN, NZERO, FE_INVALID); + NAN, NZERO, NPY_FPE_INVALID); /* sign of imaginary part is unspecified */ TEST_UNSPECIFIED2(ccosh, 0, NAN, NAN, 0, NAN, NZERO); TEST_UNSPECIFIED2(ccosh, NZERO, NAN, NAN, 0, NAN, NZERO); - TEST_RAISES(ccosh, 2.0, INFINITY, NAN, NAN, FE_INVALID); - TEST_RAISES(ccosh, 2.0, -INFINITY, NAN, NAN, FE_INVALID); - TEST_RAISES(ccosh, -2.0, INFINITY, NAN, NAN, FE_INVALID); - TEST_RAISES(ccosh, -2.0, -INFINITY, NAN, NAN, FE_INVALID); + TEST_RAISES(ccosh, 2.0, INFINITY, NAN, NAN, NPY_FPE_INVALID); + TEST_RAISES(ccosh, 2.0, -INFINITY, NAN, NAN, NPY_FPE_INVALID); + TEST_RAISES(ccosh, -2.0, INFINITY, NAN, NAN, NPY_FPE_INVALID); + TEST_RAISES(ccosh, -2.0, -INFINITY, NAN, NAN, NPY_FPE_INVALID); /* can raise FE_INVALID or not */ TEST_EE(ccosh, 2.0, NAN, NAN, NAN); @@ -1308,7 +1356,7 @@ RETTYPE ADDSUFFIX(test_ccosh)(void) /* sign of real part is unspecified */ TEST_RAISES_UNSPECIFIED2(ccosh, INFINITY, INFINITY, INFINITY, NAN, \ - -INFINITY, NAN, FE_INVALID); + -INFINITY, NAN, NPY_FPE_INVALID); TEST_EE(ccosh, INFINITY, NAN, INFINITY, NAN); TEST_EE(ccosh, -INFINITY, NAN, INFINITY, NAN); @@ -1341,16 +1389,16 @@ RETTYPE ADDSUFFIX(test_csinh)(void) /* sign of real part is unspecified */ TEST_RAISES_UNSPECIFIED2(csinh, 0, INFINITY, 0, NAN, \ - NZERO, NAN, FE_INVALID); + NZERO, NAN, NPY_FPE_INVALID); /* sign of real part is unspecified */ TEST_UNSPECIFIED2(csinh, 0, NAN, 0, NAN, NZERO, NAN); TEST_UNSPECIFIED2(csinh, NZERO, NAN, 0, NAN, NZERO, NAN); - TEST_RAISES(csinh, 2.0, INFINITY, NAN, NAN, FE_INVALID); - TEST_RAISES(csinh, 2.0, -INFINITY, NAN, NAN, FE_INVALID); - TEST_RAISES(csinh, -2.0, INFINITY, NAN, NAN, FE_INVALID); - TEST_RAISES(csinh, -2.0, -INFINITY, NAN, NAN, FE_INVALID); + TEST_RAISES(csinh, 2.0, INFINITY, NAN, NAN, NPY_FPE_INVALID); + TEST_RAISES(csinh, 2.0, -INFINITY, NAN, NAN, NPY_FPE_INVALID); + TEST_RAISES(csinh, -2.0, INFINITY, NAN, NAN, NPY_FPE_INVALID); + TEST_RAISES(csinh, -2.0, -INFINITY, NAN, NAN, NPY_FPE_INVALID); /* can raise FE_INVALID or not */ TEST_EE(csinh, 2.0, NAN, NAN, NAN); @@ -1380,7 +1428,7 @@ RETTYPE ADDSUFFIX(test_csinh)(void) /* sign of real part is unspecified */ TEST_RAISES_UNSPECIFIED2(csinh, INFINITY, INFINITY, INFINITY, NAN, \ - -INFINITY, NAN, FE_INVALID); + -INFINITY, NAN, NPY_FPE_INVALID); /* sign of real part is unspecified */ TEST_UNSPECIFIED2(csinh, INFINITY, NAN, INFINITY, NAN, -INFINITY, NAN); @@ -1411,8 +1459,8 @@ RETTYPE ADDSUFFIX(test_ctanh)(void) TEST_EE(ctanh, NZERO, 0, NZERO, 0); TEST_EE(ctanh, NZERO, NZERO, NZERO, NZERO); - TEST_RAISES(ctanh, 2.0, INFINITY, NAN, NAN, FE_INVALID); - TEST_RAISES(ctanh, -2.0, INFINITY, NAN, NAN, FE_INVALID); + TEST_RAISES(ctanh, 2.0, INFINITY, NAN, NAN, NPY_FPE_INVALID); + TEST_RAISES(ctanh, -2.0, INFINITY, NAN, NAN, NPY_FPE_INVALID); /* can raise FE_INVALID or not */ TEST_EE(ctanh, 2.0, NAN, NAN, NAN); @@ -1466,8 +1514,8 @@ RETTYPE ADDSUFFIX(test_cexp)(void) TEST_EE(cexp, NZERO, 0, 1, 0); TEST_EE(cexp, NZERO, NZERO, 1, NZERO); - TEST_RAISES(cexp, 2.0, INFINITY, NAN, NAN, FE_INVALID); - TEST_RAISES(cexp, 2.0, -INFINITY, NAN, NAN, FE_INVALID); + TEST_RAISES(cexp, 2.0, INFINITY, NAN, NAN, NPY_FPE_INVALID); + TEST_RAISES(cexp, 2.0, -INFINITY, NAN, NAN, NPY_FPE_INVALID); /* can raise FE_INVALID or not */ TEST_EE(cexp, 42.0, NAN, NAN, NAN); @@ -1501,7 +1549,7 @@ RETTYPE ADDSUFFIX(test_cexp)(void) /* sign of real part is unspecifed */ TEST_RAISES_UNSPECIFIED2(cexp, INFINITY, INFINITY, INFINITY, \ - NAN, -INFINITY, NAN, FE_INVALID); + NAN, -INFINITY, NAN, NPY_FPE_INVALID); /* signs of both parts are unspecified */ TEST_UNSPECIFIED4(cexp, -INFINITY, NAN, 0, 0, NZERO, 0, \ @@ -1534,11 +1582,11 @@ RETTYPE ADDSUFFIX(test_clog)(void) { INIT_FUNC(); /* clog(conj(z)) = conj(clog(z)) */ - TEST_RAISES(clog, NZERO, 0, -INFINITY, ADDSUFFIX(NPY_PI), FE_DIVBYZERO); - TEST_RAISES(clog, NZERO, NZERO, -INFINITY, -ADDSUFFIX(NPY_PI), FE_DIVBYZERO); + TEST_RAISES(clog, NZERO, 0, -INFINITY, ADDSUFFIX(NPY_PI), NPY_FPE_DIVIDEBYZERO); + TEST_RAISES(clog, NZERO, NZERO, -INFINITY, -ADDSUFFIX(NPY_PI), NPY_FPE_DIVIDEBYZERO); - TEST_RAISES(clog, 0, 0, -INFINITY, 0, FE_DIVBYZERO); - TEST_RAISES(clog, 0, NZERO, -INFINITY, NZERO, FE_DIVBYZERO); + TEST_RAISES(clog, 0, 0, -INFINITY, 0, NPY_FPE_DIVIDEBYZERO); + TEST_RAISES(clog, 0, NZERO, -INFINITY, NZERO, NPY_FPE_DIVIDEBYZERO); TEST_EC(clog, 2.0, INFINITY, INFINITY, ADDSUFFIX(NPY_PI_2)); TEST_EC(clog, 2.0, -INFINITY, INFINITY, -ADDSUFFIX(NPY_PI_2)); From 503047bde032c7acf8677915bceee95651692b03 Mon Sep 17 00:00:00 2001 From: Eric Moore Date: Wed, 6 Nov 2013 23:50:32 -0500 Subject: [PATCH 35/35] MAINT: move test_c99complex.c to npymath --- numpy/core/setup.py | 3 ++- numpy/core/src/npymath/npymath_tests.c | 6 +++--- numpy/core/{ => src/npymath}/test_c99complex.c | 0 3 files changed, 5 insertions(+), 4 deletions(-) rename numpy/core/{ => src/npymath}/test_c99complex.c (100%) diff --git a/numpy/core/setup.py b/numpy/core/setup.py index 2133859fdcf9..e05253659be1 100644 --- a/numpy/core/setup.py +++ b/numpy/core/setup.py @@ -233,7 +233,8 @@ def check_prec(prec): exists.extend(C99_COMPLEX_FUNCS_CHECKED) if len(exists) > 0: - fp = open(join('.', 'numpy', 'core', 'test_c99complex.c'), 'r') + fp = open(join('.', 'numpy', 'core', 'src', 'npymath', + 'test_c99complex.c'), 'r') obody = fp.read() fp.close() precname = {'f':'FLOAT', '':'DOUBLE', 'l':'LONGDOUBLE'}[prec] diff --git a/numpy/core/src/npymath/npymath_tests.c b/numpy/core/src/npymath/npymath_tests.c index 2b6be50c7643..6c4be49b7102 100644 --- a/numpy/core/src/npymath/npymath_tests.c +++ b/numpy/core/src/npymath/npymath_tests.c @@ -25,15 +25,15 @@ #define HAVE_NUMPY 1 #define FLOAT 1 -#include "../../test_c99complex.c" +#include "test_c99complex.c" #undef FLOAT #define DOUBLE 1 -#include "../../test_c99complex.c" +#include "test_c99complex.c" #undef DOUBLE #define LONGDOUBLE 1 -#include "../../test_c99complex.c" +#include "test_c99complex.c" #undef LONGDOUBLE #define TESTFUNC_INT(func, suffix) \ diff --git a/numpy/core/test_c99complex.c b/numpy/core/src/npymath/test_c99complex.c similarity index 100% rename from numpy/core/test_c99complex.c rename to numpy/core/src/npymath/test_c99complex.c