From owner-freebsd-bugs@FreeBSD.ORG Sun Jul 29 21:10:13 2012 Return-Path: Delivered-To: freebsd-bugs@hub.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [69.147.83.52]) by hub.freebsd.org (Postfix) with ESMTP id ED470106566B for ; Sun, 29 Jul 2012 21:10:13 +0000 (UTC) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (freefall.freebsd.org [IPv6:2001:4f8:fff6::28]) by mx1.freebsd.org (Postfix) with ESMTP id D75E18FC0A for ; Sun, 29 Jul 2012 21:10:13 +0000 (UTC) Received: from freefall.freebsd.org (localhost [127.0.0.1]) by freefall.freebsd.org (8.14.5/8.14.5) with ESMTP id q6TLADr1074335 for ; Sun, 29 Jul 2012 21:10:13 GMT (envelope-from gnats@freefall.freebsd.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.14.5/8.14.5/Submit) id q6TLADC4074334; Sun, 29 Jul 2012 21:10:13 GMT (envelope-from gnats) Date: Sun, 29 Jul 2012 21:10:13 GMT Message-Id: <201207292110.q6TLADC4074334@freefall.freebsd.org> To: freebsd-bugs@FreeBSD.org From: Stephen Montgomery-Smith Cc: Subject: Re: bin/170206: [msun] [patch] complex arcsinh, log, etc. X-BeenThere: freebsd-bugs@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list Reply-To: Stephen Montgomery-Smith List-Id: Bug reports List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 29 Jul 2012 21:10:14 -0000 The following reply was made to PR bin/170206; it has been noted by GNATS. From: Stephen Montgomery-Smith To: bug-followup@FreeBSD.org, stephen@FreeBSD.org Cc: Subject: Re: bin/170206: [msun] [patch] complex arcsinh, log, etc. Date: Sun, 29 Jul 2012 16:07:17 -0500 This is a multi-part message in MIME format. --------------070709080600030802090207 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit I found a bug in catanh when |z| is very large. I believe I have corrected the bug in catrig.c. I made some other small changes also, mostly style and comments. --------------070709080600030802090207 Content-Type: text/x-csrc; name="catrig.c" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="catrig.c" #include #include #include #include "math_private.h" /* * gcc doesn't implement complex multiplication or division correctly, * so we need to handle infinities specially. We turn on this pragma to * notify conforming c99 compilers that the fast-but-incorrect code that * gcc generates is acceptable, since the special cases have already been * handled. */ #pragma STDC CX_LIMITED_RANGE ON double complex clog(double complex z); static const double one = 1.00000000000000000000e+00, /* 0x3FF00000, 0x00000000 */ huge= 1.00000000000000000000e+300; /* * Testing indicates that all these functions are accurate up to 4 ULP. */ /* * 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 * * casinh(x+I*y) = sign(x)*log(A+sqrt(A*A-1)) + sign(y)*I*asin(B) * where * A = 0.5(|z+I| + |z-I|) = f(x,1+y) + f(x,1-y) + 1 * B = 0.5(|z+I| - |z-I|) * z = x+I*y * f(x,y) = 0.5*(hypot(x,y)-y) * * We also use * asin(B) = atan2(sqrt(A*A-y*y),y) * A-y = f(x,y+1) + f(x,y-1). * * Much of the difficulty comes because computing f(x,y) may produce * underflows. */ /* * Returns 0.5*(hypot(x,y)-y). It assumes x is positive, and that y does * not satisfy the inequalities 0 < fabs(y) < 1e-20. * If reporting the answer risks an underflow, the underflow flag is set, * and it returns 0.5*(hypot(x,y)-y)/x/x. */ inline static double f(double x, double y, int *underflow) { if (x==0) { *underflow = 0; if (y > 0) return 0; return -y; } if (y==0) { *underflow = 0; return 0.5*x; } if (x < 1e-100 && x < y) { *underflow = 1; return 0.5/(hypot(x,y)+y); } if (x < y) { *underflow = 0; return 0.5*x*x/(hypot(x,y)+y); } *underflow = 0; return 0.5*(hypot(x,y)-y); } /* * All the hard work is contained in this function. * Upon return: * rx = Re(casinh(x+I*y)) * B_is_usable is set to 1 if the value of B is usable. * If B_is_usable is set to 0, A2my2 = A*A-y*y. */ inline static void do_hard_work(double x, double y, double *rx, int *B_is_usable, double *B, double *A2my2) { double R, S, A, fp, fm; int fpuf, fmuf; R = hypot(x,y+1); S = hypot(x,y-1); A = 0.5*(R + S); /* * The paper by Hull et al suggests using A < 1.5. Experiment * suggested A < 10 worked better. */ if (A < 10 && isfinite(A)) { fp = f(x,1+y,&fpuf); fm = f(x,1-y,&fmuf); if (fpuf == 1 && fmuf == 1) { if (huge+x>one) /* set inexact flag. */ *rx = log1p(x*sqrt((fp+fm)*(A+1))); } else if (fmuf == 1) { /* * Overflow not possible because fp < 1e50 and * x > 1e-100. * Underflow not possible because either fm=0 or fm * approximately bigger than 1e-200. */ if (huge+x>one) /* set inexact flag. */ *rx = log1p(fp+sqrt(x)*sqrt((fp/x+fm*x)*(A+1))); } else if (fpuf == 1) { /* Similar arguments against over/underflow. */ if (huge+x>one) /* set inexact flag. */ *rx = log1p(fm+sqrt(x)*sqrt((fm/x+fp*x)*(A+1))); } else { *rx = log1p(fp + fm + sqrt((fp+fm)*(A+1))); } } else if (isinf(y)) *rx = y; else *rx = log(A + sqrt(A*A-1)); if (!isfinite(y)) { *B_is_usable = 0; if (isfinite(x)) *A2my2 = 0; else if (isnan(x)) *A2my2 = x; else *A2my2 = y; return; } if (isnan(x) && y == 0) { *B_is_usable = 0; *A2my2 = 1; return; } *B = y/A; /* = 0.5*(R - S) */ *B_is_usable = 1; /* * The paper by Hull et al suggests using B > 0.6417. I just made up * the number 0.5. It seems to work. */ if (*B > 0.5) { *B_is_usable = 0; fp = f(x,y+1,&fpuf); fm = f(x,y-1,&fmuf); if (fpuf == 1 && fmuf == 1) *A2my2 =x*sqrt((A+y)*(fp+fm)); else if (fmuf == 1) /* * Overflow not possible because fp < 1e50 and * x > 1e-100. * Underflow not possible because either fm=0 or fm * approximately bigger than 1e-200. */ *A2my2 = sqrt(x)*sqrt((A+y)*(fp/x+fm*x)); else if (fpuf == 1) /* Similar arguments against over/underflow. */ *A2my2 = sqrt(x)*sqrt((A+y)*(fm/x+fp*x)); else *A2my2 = sqrt((A+y)*(fp+fm)); } } double complex casinh(double complex z) { double x, y, rx, ry, B, A2my2; int sx, sy; int B_is_usable; x = creal(z); y = cimag(z); sx = signbit(x); sy = signbit(y); x = fabs(x); y = fabs(y); if (cabs(z) > 1e20 && isfinite(x) && isfinite(y)) { if (huge+x>one) { /* set inexact flag. */ if (sx == 0) return clog(2*z); if (sx == 1) return -clog(-2*z); } } if (cabs(z) < 1e-20) if (huge+x>one) /* set inexact flag. */ return z; do_hard_work(x, y, &rx, &B_is_usable, &B, &A2my2); if (B_is_usable) ry = asin(B); else ry = atan2(y,A2my2); if (sx == 1) rx = copysign(rx, -1); /* casinh is odd. */ if (sy == 1) ry = copysign(ry, -1); /* casinh(conj(z)) = conj(casinh(z)). */ return cpack(rx,ry); } /* * casin(z) = reverse(casinh(reverse(z))) * where reverse(x+I*y) = y+x*I = I*conj(x+I*y). */ double complex casin(double complex z) { complex w; w = casinh(cpack(cimag(z),creal(z))); return cpack(cimag(w),creal(w)); } /* * cacos(z) = PI/2 - casin(z) * but do the computation carefully so cacos(z) is accurate when z is * close to 1. */ double complex cacos(double complex z) { double x, y, rx, ry, B, A2my2; int sx, sy; int B_is_usable; complex w; x = creal(z); y = cimag(z); sx = signbit(x); sy = signbit(y); x = fabs(x); y = fabs(y); if (cabs(z) > 1e20 && isfinite(x) && isfinite(y)) { if (huge+x>one) { /* set inexact flag. */ w = clog(2*z); if (signbit(cimag(w)) == 0) return cpack(cimag(w),-creal(w)); return cpack(-cimag(w),creal(w)); } } if (cabs(z) < 1e-10) if (huge+x>one) { /* set inexact flag. */ if (signbit(cimag(z)) == 0) return cpack(M_PI_2-creal(z),copysign(cimag(z),-1)); return cpack(M_PI_2-creal(z),copysign(cimag(z),1)); } do_hard_work(y, x, &ry, &B_is_usable, &B, &A2my2); if (B_is_usable) { if (sx==0) rx = acos(B); else rx = acos(-B); } else { if (sx==0) rx = atan2(A2my2,x); else rx = atan2(A2my2,-x); } if (sy==0) ry = copysign(ry, -1); /* cacos(conj(z)) = conj(cacos(z)). */ return cpack(rx,ry); } /* * cacosh(z) = I*cacos(z) or -I*cacos(z) * where the sign is chosen so Re(cacosh(z)) >= 0. */ double complex cacosh(double complex z) { double complex w; w = cacos(z); if (signbit(cimag(w)) == 0) return cpack(cimag(w),copysign(creal(w),-1)); else return cpack(-cimag(w),creal(w)); } /* * catanh(z) = 0.5 * log((1+z)/(1-z)) * = 0.25 * log(|z+1|^2/|z-1|^2) + 0.5 * I * atan2(2y, (1-x*x-y*y)) * * Note that |z+1|^2/|z-1|^2 = 1 + 4*x/|z-1|^2 * = 1 / ( 1 - 4*x/|z+1|^2 ) * * If |z| is large, then * catanh(z) appprox = 1/z + sign(y)*I*PI/2 */ double complex catanh(double complex z) { double x, y, rx, ry; double zp1_2, zm1_2; /* |z+1|^2 and |z-1|^2 */ x = creal(z); y = cimag(z); if (cabs(z) < 1e-20) if (huge+x>one) /* set inexact flag. */ return z; if (isinf(x) && isnan(y)) return cpack(copysign(0, x), y); if (isnan(x) && isinf(y)) return cpack(copysign(0, x), copysign(M_PI_2,y)); if (x == 0 && isnan(y)) return cpack(x, y); if (isnan(x) || isnan(y)) return clog(z); if (isinf(x) || isinf(y)) return cpack(copysign(0,x),copysign(M_PI_2,y)); if (cabs(z) > 1e20) if (huge+x>one) { /* set inexact flag. */ if (x==0) return cpack(copysign(0,x),copysign(M_PI_2,y)); else return 1/z + cpack(0,copysign(M_PI_2,y)); } if (fabs(y) < 1e-100) { if (huge+x>one) { /* set inexact flag. */ zp1_2 = (x+1)*(x+1); zm1_2 = (x-1)*(x-1); } } else { zp1_2 = (x+1)*(x+1)+y*y; zm1_2 = (x-1)*(x-1)+y*y; } if (zp1_2 < 0.5 || zm1_2 < 0.5) rx = 0.25*(log(zp1_2/zm1_2)); else if (x == 0) rx = x; else if (x > 0) rx = 0.25*log1p(4*x/zm1_2); else rx = -0.25*log1p(-4*x/zp1_2); if (x==1 || x==-1) { if (y==0) ry = y; else if (signbit(y) == 0) ry = atan2(2, -y)/2; else ry = atan2(-2, y)/2; } else if (fabs(y) < 1e-100) { if (huge+x>one) /* set inexact flag. */ ry = atan2(2*y, (1-x)*(1+x))/2; } else ry = atan2(2*y, (1-x)*(1+x)-y*y)/2; return cpack(rx,ry); } /* * catan(z) = reverse(catanh(reverse(z))) * where reverse(x+I*y) = y+x*I = I*conj(x+I*y). */ double complex catan(double complex z) { complex w; w = catanh(cpack(cimag(z),creal(z))); return cpack(cimag(w),creal(w)); } --------------070709080600030802090207--