bin/170206: complex arcsinh, log, etc.

Stephen Montgomery-Smith stephen at missouri.edu
Sat Jul 28 15:50:12 UTC 2012


The following reply was made to PR bin/170206; it has been noted by GNATS.

From: Stephen Montgomery-Smith <stephen at missouri.edu>
To: Bruce Evans <brde at optusnet.com.au>
Cc: freebsd-bugs at freebsd.org, FreeBSD-gnats-submit at freebsd.org,
        Stephen Montgomery-Smith <stephen at freebsd.org>
Subject: Re: bin/170206: complex arcsinh, log, etc.
Date: Sat, 28 Jul 2012 10:46:30 -0500

 This is a multi-part message in MIME format.
 --------------060304040300070501030603
 Content-Type: text/plain; charset=ISO-8859-1; format=flowed
 Content-Transfer-Encoding: 7bit
 
 OK.  This clog really seems to work.
 
 x*x + y*y - 1 is computed with a ULP less than 0.8.  The rest of the 
 errors seem to be due to the implementation of log1p.  The ULP of the 
 final answer seems to be never bigger than a little over 2.
 
 
 
 --------------060304040300070501030603
 Content-Type: text/x-csrc;
  name="cplex.c"
 Content-Transfer-Encoding: 7bit
 Content-Disposition: attachment;
  filename="cplex.c"
 
 #include <stdio.h>
 #include <string.h>
 #include <complex.h>
 #include <float.h>
 #include <math.h>
 
 #include "math_private.h"
 
 /* Get binary digits -d through -d-16.  Assume x > 0 */
 uint32_t get_bits(double x, int d) {
 	uint32_t hi, lo;
 	int e;
 
 	if (x == 0) return 0;
 	e = d+ilogb(x)-4;
 	EXTRACT_WORDS(hi, lo, x);
 	hi &= 0x000fffff;
 	hi |= 0x00100000;
 	if (e <= -32) return 0;
 	if (e <= 0) {
 		hi >>= -e;
 		return hi & 0xffff;
 	}
 	if (e < 32) {
 		hi <<= e;
 		lo >>= (32-e);
 		return (hi | lo) & 0xffff;
 	}
 	if (e == 32)
 		return lo & 0xffff;
 	if (e <= 63) {
 		lo <<= (e-32);
 		return lo & 0xffff;
 	}
 	return 0;
 }
 
 #define NR_BLOCKS 10
 
 double complex
 clog(double complex z)
 {
 	double x, y;
 	double ax, ay, t, hm1;
 	uint64_t xx[NR_BLOCKS+1], yy[NR_BLOCKS+1];
 	uint64_t zz[NR_BLOCKS+1];
 	uint64_t carry;
 	int sign;
 	int i, j;
 
 	x = creal(z);
 	y = cimag(z);
 
 	/* Handle NaNs using the general formula to mix them right. */
 	if (x != x || y != y)
 		return (cpack(log(hypot(x, y)), atan2(y, x)));
 
 	ax = fabs(x);
 	ay = fabs(y);
 	if (ax < ay) {
 		t = ax;
 		ax = ay;
 		ay = t;
 	}
 
 	/*
 	 * To avoid unnecessary overflow, if x or y are very large, divide x
 	 * and y by M_E, and then add 1 to the logarithm.  This depends on
 	 * M_E being larger than sqrt(2).
 	 * There is a potential loss of accuracy caused by dividing by M_E,
 	 * but this case should happen extremely rarely.
 	 */
 	if (ay > 5e307)
 		return (cpack(log(hypot(x / M_E, y / M_E)) + 1, atan2(y, x)));
 
 	if (ax == 1) {
 		if (ay < 1e-150)
 			return (cpack((ay * 0.5) * ay, atan2(y, x)));
 		return (cpack(log1p(ay * ay) * 0.5, atan2(y, x)));
 	}
 
 	/*
 	 * Because atan2 and hypot conform to C99, this also covers all the
 	 * edge cases when x or y are 0 or infinite.
 	 */
 	if (ax < 1e-50 || ay < 1e-50 || ax > 1e50 || ay > 1e50)
 		return (cpack(log(hypot(x, y)), atan2(y, x)));
 
 	/* 
 	 * From this point on, we don't need to worry about underflow or
 	 * overflow in calculating ax*ax or ay*ay.
 	 */
 
 	/* Some easy cases. */
 
 	if (ax*ax + ay*ay <= 0.1 || ax*ax + ay*ay >= 10)
 		return (cpack(log(ax*ax + ay*ay) * 0.5, atan2(y, x)));
 
 	/*
 	 * Take extra care so that ULP of real part is small if hypot(x,y) is
 	 * moderately close to 1.  We compute ax*ax + ay*ay - 1 using
 	 * long multiplication in base 2^16.
 	 */
 
 	/*
 	 * Split ax and ay into fixed point numbers with 160 bits after the
 	 * "decimal" point.
 	 */
 	for (i=-1; i<NR_BLOCKS; i++) {
 		xx[i+1] = get_bits(ax,16*i);
 		yy[i+1] = get_bits(ay,16*i);
 	}
 
 	/*
 	 * Long multiplication.  We use 64 bit integers instead of 32 bit
 	 * because we might get slightly bigger than 32 bit numbers due to
 	 * the additions.  (But probably 36 bit integers would be more than
 	 * enough.)
 	 */
 	memset(zz,0,sizeof(zz));
 	for (i=-1; i<NR_BLOCKS; i++)
 		for (j=-1; j<NR_BLOCKS && i+j+1 < NR_BLOCKS; j++) {
 			zz[i+j+2] += xx[i+1]*xx[j+1];
 			zz[i+j+2] += yy[i+1]*yy[j+1];
 		}
 	/* Subtract 1. */
 	zz[0]--;
 
 	/* Handle the carries. */
 	carry = 0;
 	for (i=NR_BLOCKS-1; i>=-1; i--) {
 		zz[i+1] += carry;
 		carry = zz[i+1] >> 16;
 		zz[i+1] &= 0xffff;
 	}
 
 	/*
 	 * If the number is negative, compute the 1's complement.  (We
 	 * should compute the 2's complement, but the the error will be 
 	 * negligable.)
 	 */
 	if ((zz[0] & 0x8000) != 0) {
 		sign = 1;
 		for (i=-1; i<NR_BLOCKS; i++)
 			zz[i+1] = 0xffff & (~zz[i+1]);
 	} else
 		sign = 0;
 
 	/* Convert fixed point into floating point. */
 	hm1 = 0;
 	for (i=NR_BLOCKS-1; i>=-1; i--)
 		hm1 += zz[i+1] * exp2(16*(-1-i));
 
 	if (sign == 1) hm1 = -hm1;
 
 	return (cpack(0.5 * log1p(hm1), atan2(y, x)));
 }
 
 float complex
 clogf(float complex z)
 {
 	return clog(z);
 }
 
 
 --------------060304040300070501030603--


More information about the freebsd-bugs mailing list