Last active
December 15, 2015 16:19
-
-
Save ewmoore/5288394 to your computer and use it in GitHub Desktop.
Sanity Checks for c99 complex math functions.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <fenv.h> | |
| #include <float.h> | |
| #include <stdio.h> | |
| #include <math.h> | |
| #include <complex.h> | |
| const double NZERO = 1.0 * 0.0; | |
| #define STRINGIZE_INT(A) #A | |
| #define STRINGIZE(A) STRINGIZE_INT(A) | |
| #define TEST_PRINTF(func, x, e, r) printf("%d: " STRINGIZE(func) "(%g + %gj): expected: %g + %gj: received: %g + %gj\n", __LINE__, creal(x), cimag(x), creal(e), cimag(e), creal(r), cimag(r)) | |
| #define TEST(func, x, e) do { \ | |
| double complex r = func(x); \ | |
| if (!cd_isclose(r, e)) { \ | |
| ret = 0; \ | |
| TEST_PRINTF(func, x, e, r); \ | |
| } \ | |
| } \ | |
| while(0) | |
| #define TEST_RAISES(func, x, e, fpe) do { \ | |
| int except; \ | |
| double complex r; \ | |
| feclearexcept(FE_ALL_EXCEPT); \ | |
| r = func(x); \ | |
| except = fetestexcept(fpe); \ | |
| if (!(except & fpe && cd_isclose(r, e))) { \ | |
| ret = 0; \ | |
| TEST_PRINTF(func, x, e, r); \ | |
| } \ | |
| } \ | |
| while(0) | |
| double complex cpack(double r, double i) | |
| { | |
| union { | |
| double complex z; | |
| double a[2]; | |
| } z1; | |
| z1.a[0] = r; | |
| z1.a[1] = i; | |
| return z1.z; | |
| } | |
| int isclose(double a, double b) | |
| { | |
| double atol = 0.0; | |
| double rtol = 1e-7; | |
| if (isfinite(a) && isfinite(b)) { | |
| return fabs(a - b) <= (atol + rtol*fabs(b)); | |
| } | |
| else if (isinf(a) && isinf(b)) { | |
| return (signbit(a) == signbit(b)); | |
| } | |
| else { | |
| return (isnan(a) && isnan(b)); | |
| } | |
| } | |
| int cd_isclose(double complex a, double complex b) | |
| { | |
| double ar = creal(a); | |
| double ai = cimag(a); | |
| double br = creal(b); | |
| double bi = cimag(b); | |
| return isclose(ar, br) && isclose(ai, bi); | |
| } | |
| int cd_isnan(double complex x) | |
| { | |
| return isnan(creal(x)) && isnan(cimag(x)); | |
| } | |
| int cd_isinf(double complex x) | |
| { | |
| return isinf(creal(x)) && isinf(cimag(x)); | |
| } | |
| int check_branch_cut(double complex (*func)(double complex), double complex x0, double complex dx, double re_sign, double im_sign, int sig_zero_ok) | |
| { | |
| double scale = DBL_EPSILON * 1e3; | |
| double atol = 1e-4; | |
| double complex y0 = func(x0); | |
| double complex yp = func(x0 + dx*scale*cabs(x0)/cabs(dx)); | |
| double complex ym = func(x0 - dx*scale*cabs(x0)/cabs(dx)); | |
| if (fabs(creal(y0) - creal(yp)) >= atol) | |
| return 0; | |
| if (fabs(cimag(y0) - cimag(yp)) >= atol) | |
| return 0; | |
| if (fabs(creal(y0) - re_sign*creal(ym)) >= atol) | |
| return 0; | |
| if (fabs(cimag(y0) - im_sign*cimag(ym)) >= atol) | |
| return 0; | |
| if (sig_zero_ok) { /* check that signed zeros also work as a displacement. */ | |
| return 0; | |
| } | |
| return 1; | |
| } | |
| /* | |
| * We assume that if we make it here at all, we have c99 complex support in | |
| * the compiler that we are using. | |
| * | |
| * We define a single test_fname function for each function that will be | |
| * tested. These function return 1 if the tests pass and 0 if the tests | |
| * fail. These are go/no go tests and which test failed is not reported. | |
| */ | |
| /*========================================================== | |
| * clog | |
| *=========================================================*/ | |
| #define TEST_CLOG(x, e) TEST(clog, x, e) | |
| #define TEST_RAISES_CLOG(x, e, fpe) TEST_RAISES(clog, x, e, fpe) | |
| int test_clog() | |
| { | |
| int ret = 1; | |
| const double NZERO = -1.0 * 0.0; | |
| /* tests from test_umath.py: TestComplexFunctions: test_it | |
| */ | |
| TEST_CLOG(0.5, log(0.5)); | |
| /* tests from test_umath_complex.py: TestCLog: test_simple | |
| */ | |
| TEST_CLOG(1, 0); | |
| TEST_CLOG(1 + 2*I, 0.80471895621705014 + 1.1071487177940904*I); | |
| /* tests from test_umath_complex.py: TestCLog: test_special_values | |
| * These are testing the the value in Appendix G. | |
| */ | |
| TEST_RAISES_CLOG(cpack(NZERO, 0), cpack(-INFINITY, M_PI), FE_DIVBYZERO); | |
| TEST_RAISES_CLOG(cpack(0, 0), cpack(-INFINITY, 0), FE_DIVBYZERO); | |
| TEST_CLOG(cpack(1, INFINITY), cpack(INFINITY, M_PI_2)); | |
| TEST_CLOG(cpack(1, NAN), cpack(NAN, NAN)); /* can raise FE_INVALID or not. */ | |
| TEST_CLOG(cpack(-INFINITY, 1), cpack(+INFINITY, M_PI)); | |
| TEST_CLOG(cpack(+INFINITY, 1), cpack(+INFINITY, 0)); | |
| TEST_CLOG(cpack(-INFINITY, +INFINITY), cpack(INFINITY, 0.75 * M_PI)); | |
| TEST_CLOG(cpack(+INFINITY, +INFINITY), cpack(INFINITY, 0.25 * M_PI)); | |
| TEST_CLOG(cpack(-INFINITY, NAN), cpack(INFINITY, NAN)); | |
| TEST_CLOG(cpack(+INFINITY, NAN), cpack(INFINITY, NAN)); | |
| TEST_CLOG(cpack(NAN, 1), cpack(NAN, NAN)); /* can raise FE_INVALID or not */ | |
| TEST_CLOG(cpack(NAN, INFINITY), cpack(INFINITY, NAN)); | |
| TEST_CLOG(cpack(NAN, NAN), cpack(NAN, NAN)); | |
| /* clog(conj(z)) = conj(clog(z)) */ | |
| /* tests concerning the sign of nan are not included. */ | |
| TEST_RAISES_CLOG(cpack(NZERO, NZERO), cpack(-INFINITY, -M_PI), FE_DIVBYZERO); | |
| TEST_RAISES_CLOG(cpack(0, NZERO), cpack(-INFINITY, NZERO), FE_DIVBYZERO); | |
| TEST_CLOG(cpack(1, -INFINITY), cpack(INFINITY, -M_PI_2)); | |
| TEST_CLOG(cpack(-INFINITY, -1), cpack(+INFINITY, -M_PI)); | |
| TEST_CLOG(cpack(+INFINITY, -1), cpack(+INFINITY, NZERO)); | |
| TEST_CLOG(cpack(-INFINITY, -INFINITY), cpack(INFINITY, -0.75 * M_PI)); | |
| TEST_CLOG(cpack(+INFINITY, -INFINITY), cpack(INFINITY, -0.25 * M_PI)); | |
| TEST_CLOG(cpack(NAN, -INFINITY), cpack(INFINITY, NAN)); | |
| if(!check_branch_cut(clog, -0.5, 1*I, 1, -1, 0)) { | |
| ret = 0; | |
| printf("clog branch cut bad!\n"); | |
| } | |
| if(!check_branch_cut(clog, -0.5, 1*I, 1, -1, 1)) { | |
| ret = 0; | |
| printf("clog branch cut bad! 2\n"); | |
| } | |
| return ret; | |
| } | |
| #undef TEST_CLOG | |
| #undef TEST_RAISES_CLOG | |
| /*========================================================== | |
| * ctanh | |
| *=========================================================*/ | |
| #define TEST_CTANH(x, e) TEST(ctanh, x, e) | |
| #define TEST_RAISES_CTANH(x, e, fpe) TEST_RAISES(ctanh, x, e, fpe) | |
| int test_ctanh() | |
| { | |
| int ret = 1; | |
| /**** Test Appendix G special values ****/ | |
| TEST_CTANH(cpack(0.0, 0.0), cpack(0.0, 0.0)); | |
| TEST_RAISES_CTANH(cpack(1.0, INFINITY), cpack(NAN, NAN), FE_INVALID); | |
| /* can raise FE_INVALID or not */ | |
| TEST_CTANH(cpack(1.0, NAN), cpack(NAN, NAN)); | |
| /* only for positive, nonzero imag part */ | |
| TEST_CTANH(cpack(INFINITY, 1.0), cpack(1.0, 0.0)); | |
| /* only for positive nonzero imag part */ | |
| TEST_CTANH(cpack(INFINITY, 2.0), cpack(1.0, NZERO)); | |
| /* signbit(cimag(r)) is unspecified. */ | |
| TEST_CTANH(cpack(INFINITY, INFINITY), cpack(1.0, 0.0)); | |
| /* signbit(cimag(r)) is unspecified */ | |
| TEST_CTANH(cpack(INFINITY, NAN), cpack(1.0, 0.0)); | |
| TEST_CTANH(cpack(NAN, 0.0), cpack(NAN, 0.0)); | |
| /* can raise FE_INVALID or not */ | |
| TEST_CTANH(cpack(NAN, 1.0), cpack(NAN, NAN)); | |
| TEST_CTANH(cpack(NAN, NAN), cpack(NAN, NAN)); | |
| /* ctanh(conj(z)) = conj(ctanh(z)) */ | |
| TEST_CTANH(cpack(0.0, NZERO), cpack(0.0, NZERO)); | |
| TEST_RAISES_CTANH(cpack(1.0, -INFINITY), cpack(NAN, NAN), FE_INVALID); | |
| TEST_CTANH(cpack(NAN, NZERO), cpack(NAN, NZERO)); | |
| /* ctanh is odd */ | |
| TEST_CTANH(cpack(NZERO, NZERO), cpack(NZERO, NZERO)); | |
| TEST_RAISES_CTANH(cpack(-1.0, INFINITY), cpack(NAN, NAN), FE_INVALID); | |
| /* can raise FE_INVALID or not */ | |
| TEST_CTANH(cpack(-1.0, NAN), cpack(NAN, NAN)); | |
| /* signbit(cimag(r)) is unspecified. */ | |
| TEST_CTANH(cpack(-INFINITY, -INFINITY), cpack(-1.0, 0.0)); | |
| /* signbit(cimag(r)) is unspecified */ | |
| TEST_CTANH(cpack(-INFINITY, NAN), cpack(-1.0, 0.0)); | |
| TEST_CTANH(cpack(NAN, NZERO), cpack(NAN, NZERO)); | |
| /* can raise FE_INVALID or not */ | |
| TEST_CTANH(cpack(NAN, -1.0), cpack(NAN, NAN)); | |
| /**** Github Issue 2321: nans for large arguments ****/ | |
| TEST_CTANH(cpack(1000.0, 0.0), cpack(1.0, 0.0)); | |
| return ret; | |
| } | |
| #undef TEST_CTANH | |
| #undef TEST_RAISES_CTANH | |
| int main(int argc, char** argv) | |
| { | |
| int tclog, tctanh; | |
| tclog = test_clog(); | |
| printf("clog: %d\n\n", tclog); | |
| tctanh = test_ctanh(); | |
| printf("ctanh: %d\n\n", tctanh); | |
| return 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment