xref: /csrg-svn/lib/libm/ieee/support.c (revision 61283)
134123Sbostic /*
2*61283Sbostic  * Copyright (c) 1985, 1993
3*61283Sbostic  *	The Regents of the University of California.  All rights reserved.
434123Sbostic  *
542657Sbostic  * %sccs.include.redist.c%
624581Szliu  */
724581Szliu 
824581Szliu #ifndef lint
9*61283Sbostic static char sccsid[] = "@(#)support.c	8.1 (Berkeley) 06/04/93";
1034123Sbostic #endif /* not lint */
1124581Szliu 
1224581Szliu /*
1331828Szliu  * Some IEEE standard 754 recommended functions and remainder and sqrt for
1424581Szliu  * supporting the C elementary functions.
1524581Szliu  ******************************************************************************
1624581Szliu  * WARNING:
1724581Szliu  *      These codes are developed (in double) to support the C elementary
1824581Szliu  * functions temporarily. They are not universal, and some of them are very
1924581Szliu  * slow (in particular, drem and sqrt is extremely inefficient). Each
2024581Szliu  * computer system should have its implementation of these functions using
2124581Szliu  * its own assembler.
2224581Szliu  ******************************************************************************
2324581Szliu  *
2431828Szliu  * IEEE 754 required operations:
2524581Szliu  *     drem(x,p)
2624581Szliu  *              returns  x REM y  =  x - [x/y]*y , where [x/y] is the integer
2724581Szliu  *              nearest x/y; in half way case, choose the even one.
2824581Szliu  *     sqrt(x)
2924581Szliu  *              returns the square root of x correctly rounded according to
3024581Szliu  *		the rounding mod.
3124581Szliu  *
3231828Szliu  * IEEE 754 recommended functions:
3324581Szliu  * (a) copysign(x,y)
3424581Szliu  *              returns x with the sign of y.
3524581Szliu  * (b) scalb(x,N)
3624581Szliu  *              returns  x * (2**N), for integer values N.
3724581Szliu  * (c) logb(x)
3824581Szliu  *              returns the unbiased exponent of x, a signed integer in
3924581Szliu  *              double precision, except that logb(0) is -INF, logb(INF)
4024581Szliu  *              is +INF, and logb(NAN) is that NAN.
4124581Szliu  * (d) finite(x)
4224581Szliu  *              returns the value TRUE if -INF < x < +INF and returns
4324581Szliu  *              FALSE otherwise.
4424581Szliu  *
4524581Szliu  *
4624581Szliu  * CODED IN C BY K.C. NG, 11/25/84;
4724581Szliu  * REVISED BY K.C. NG on 1/22/85, 2/13/85, 3/24/85.
4824581Szliu  */
4924581Szliu 
5035681Sbostic #include "mathimpl.h"
5124581Szliu 
5231856Szliu #if defined(vax)||defined(tahoe)      /* VAX D format */
5331828Szliu #include <errno.h>
5435681Sbostic     static const unsigned short msign=0x7fff , mexp =0x7f80 ;
5535681Sbostic     static const short  prep1=57, gap=7, bias=129           ;
5635681Sbostic     static const double novf=1.7E38, nunf=3.0E-39, zero=0.0 ;
5731856Szliu #else	/* defined(vax)||defined(tahoe) */
5835681Sbostic     static const unsigned short msign=0x7fff, mexp =0x7ff0  ;
5935681Sbostic     static const short prep1=54, gap=4, bias=1023           ;
6035681Sbostic     static const double novf=1.7E308, nunf=3.0E-308,zero=0.0;
6131856Szliu #endif	/* defined(vax)||defined(tahoe) */
6224581Szliu 
scalb(x,N)6324581Szliu double scalb(x,N)
6424581Szliu double x; int N;
6524581Szliu {
6624581Szliu         int k;
6724581Szliu 
6831856Szliu #ifdef national
6924581Szliu         unsigned short *px=(unsigned short *) &x + 3;
7031856Szliu #else	/* national */
7124581Szliu         unsigned short *px=(unsigned short *) &x;
7231856Szliu #endif	/* national */
7324581Szliu 
7424581Szliu         if( x == zero )  return(x);
7524581Szliu 
7631856Szliu #if defined(vax)||defined(tahoe)
7724581Szliu         if( (k= *px & mexp ) != ~msign ) {
7831828Szliu             if (N < -260)
7931828Szliu 		return(nunf*nunf);
8031828Szliu 	    else if (N > 260) {
8131828Szliu 		return(copysign(infnan(ERANGE),x));
8231828Szliu 	    }
8331856Szliu #else	/* defined(vax)||defined(tahoe) */
8424581Szliu         if( (k= *px & mexp ) != mexp ) {
8524581Szliu             if( N<-2100) return(nunf*nunf); else if(N>2100) return(novf+novf);
8624581Szliu             if( k == 0 ) {
8724581Szliu                  x *= scalb(1.0,(int)prep1);  N -= prep1; return(scalb(x,N));}
8831856Szliu #endif	/* defined(vax)||defined(tahoe) */
8924581Szliu 
9024581Szliu             if((k = (k>>gap)+ N) > 0 )
9124581Szliu                 if( k < (mexp>>gap) ) *px = (*px&~mexp) | (k<<gap);
9224581Szliu                 else x=novf+novf;               /* overflow */
9324581Szliu             else
9424581Szliu                 if( k > -prep1 )
9524581Szliu                                         /* gradual underflow */
9624581Szliu                     {*px=(*px&~mexp)|(short)(1<<gap); x *= scalb(1.0,k-1);}
9724581Szliu                 else
9824581Szliu                 return(nunf*nunf);
9924581Szliu             }
10024581Szliu         return(x);
10124581Szliu }
10224581Szliu 
10324581Szliu 
copysign(x,y)10424581Szliu double copysign(x,y)
10524581Szliu double x,y;
10624581Szliu {
10731856Szliu #ifdef national
10824581Szliu         unsigned short  *px=(unsigned short *) &x+3,
10924581Szliu                         *py=(unsigned short *) &y+3;
11031856Szliu #else	/* national */
11124581Szliu         unsigned short  *px=(unsigned short *) &x,
11224581Szliu                         *py=(unsigned short *) &y;
11331856Szliu #endif	/* national */
11424581Szliu 
11531856Szliu #if defined(vax)||defined(tahoe)
11624581Szliu         if ( (*px & mexp) == 0 ) return(x);
11731856Szliu #endif	/* defined(vax)||defined(tahoe) */
11824581Szliu 
11924581Szliu         *px = ( *px & msign ) | ( *py & ~msign );
12024581Szliu         return(x);
12124581Szliu }
12224581Szliu 
logb(x)12324581Szliu double logb(x)
12424581Szliu double x;
12524581Szliu {
12624581Szliu 
12731856Szliu #ifdef national
12824581Szliu         short *px=(short *) &x+3, k;
12931856Szliu #else	/* national */
13024581Szliu         short *px=(short *) &x, k;
13131856Szliu #endif	/* national */
13224581Szliu 
13331856Szliu #if defined(vax)||defined(tahoe)
13427449Szliu         return (int)(((*px&mexp)>>gap)-bias);
13531856Szliu #else	/* defined(vax)||defined(tahoe) */
13624581Szliu         if( (k= *px & mexp ) != mexp )
13724581Szliu             if ( k != 0 )
13824581Szliu                 return ( (k>>gap) - bias );
13924581Szliu             else if( x != zero)
14024581Szliu                 return ( -1022.0 );
14124581Szliu             else
14224581Szliu                 return(-(1.0/zero));
14324581Szliu         else if(x != x)
14424581Szliu             return(x);
14524581Szliu         else
14624581Szliu             {*px &= msign; return(x);}
14731856Szliu #endif	/* defined(vax)||defined(tahoe) */
14824581Szliu }
14924581Szliu 
finite(x)15024581Szliu finite(x)
15124581Szliu double x;
15224581Szliu {
15331856Szliu #if defined(vax)||defined(tahoe)
15431828Szliu         return(1);
15531856Szliu #else	/* defined(vax)||defined(tahoe) */
15631856Szliu #ifdef national
15724581Szliu         return( (*((short *) &x+3 ) & mexp ) != mexp );
15831856Szliu #else	/* national */
15924581Szliu         return( (*((short *) &x ) & mexp ) != mexp );
16031856Szliu #endif	/* national */
16131856Szliu #endif	/* defined(vax)||defined(tahoe) */
16224581Szliu }
16324581Szliu 
drem(x,p)16424581Szliu double drem(x,p)
16524581Szliu double x,p;
16624581Szliu {
16724581Szliu         short sign;
16835681Sbostic         double hp,dp,tmp;
16924581Szliu         unsigned short  k;
17031856Szliu #ifdef national
17124581Szliu         unsigned short
17224581Szliu               *px=(unsigned short *) &x  +3,
17324581Szliu               *pp=(unsigned short *) &p  +3,
17424581Szliu               *pd=(unsigned short *) &dp +3,
17524581Szliu               *pt=(unsigned short *) &tmp+3;
17631856Szliu #else	/* national */
17724581Szliu         unsigned short
17824581Szliu               *px=(unsigned short *) &x  ,
17924581Szliu               *pp=(unsigned short *) &p  ,
18024581Szliu               *pd=(unsigned short *) &dp ,
18124581Szliu               *pt=(unsigned short *) &tmp;
18231856Szliu #endif	/* national */
18324581Szliu 
18424581Szliu         *pp &= msign ;
18524581Szliu 
18631856Szliu #if defined(vax)||defined(tahoe)
18731828Szliu         if( ( *px & mexp ) == ~msign )	/* is x a reserved operand? */
18831856Szliu #else	/* defined(vax)||defined(tahoe) */
18931827Szliu         if( ( *px & mexp ) == mexp )
19031856Szliu #endif	/* defined(vax)||defined(tahoe) */
19131827Szliu 		return  (x-p)-(x-p);	/* create nan if x is inf */
19231828Szliu 	if (p == zero) {
19331856Szliu #if defined(vax)||defined(tahoe)
19431828Szliu 		return(infnan(EDOM));
19531856Szliu #else	/* defined(vax)||defined(tahoe) */
19631828Szliu 		return zero/zero;
19731856Szliu #endif	/* defined(vax)||defined(tahoe) */
19831828Szliu 	}
19931828Szliu 
20031856Szliu #if defined(vax)||defined(tahoe)
20131828Szliu         if( ( *pp & mexp ) == ~msign )	/* is p a reserved operand? */
20231856Szliu #else	/* defined(vax)||defined(tahoe) */
20331827Szliu         if( ( *pp & mexp ) == mexp )
20431856Szliu #endif	/* defined(vax)||defined(tahoe) */
20531827Szliu 		{ if (p != p) return p; else return x;}
20624581Szliu 
20724581Szliu         else  if ( ((*pp & mexp)>>gap) <= 1 )
20824581Szliu                 /* subnormal p, or almost subnormal p */
20924581Szliu             { double b; b=scalb(1.0,(int)prep1);
21024581Szliu               p *= b; x = drem(x,p); x *= b; return(drem(x,p)/b);}
21124581Szliu         else  if ( p >= novf/2)
21224581Szliu             { p /= 2 ; x /= 2; return(drem(x,p)*2);}
21324581Szliu         else
21424581Szliu             {
21524581Szliu                 dp=p+p; hp=p/2;
21624581Szliu                 sign= *px & ~msign ;
21724581Szliu                 *px &= msign       ;
21824581Szliu                 while ( x > dp )
21924581Szliu                     {
22024581Szliu                         k=(*px & mexp) - (*pd & mexp) ;
22124581Szliu                         tmp = dp ;
22224581Szliu                         *pt += k ;
22324581Szliu 
22431856Szliu #if defined(vax)||defined(tahoe)
22524581Szliu                         if( x < tmp ) *pt -= 128 ;
22631856Szliu #else	/* defined(vax)||defined(tahoe) */
22724581Szliu                         if( x < tmp ) *pt -= 16 ;
22831856Szliu #endif	/* defined(vax)||defined(tahoe) */
22924581Szliu 
23024581Szliu                         x -= tmp ;
23124581Szliu                     }
23224581Szliu                 if ( x > hp )
23324581Szliu                     { x -= p ;  if ( x >= hp ) x -= p ; }
23424581Szliu 
23531856Szliu #if defined(vax)||defined(tahoe)
23631825Szliu 		if (x)
23731856Szliu #endif	/* defined(vax)||defined(tahoe) */
23831825Szliu 			*px ^= sign;
23924581Szliu                 return( x);
24024581Szliu 
24124581Szliu             }
24224581Szliu }
24335681Sbostic 
24435681Sbostic 
sqrt(x)24524581Szliu double sqrt(x)
24624581Szliu double x;
24724581Szliu {
24824581Szliu         double q,s,b,r;
24935681Sbostic         double t;
25035681Sbostic 	double const zero=0.0;
25135681Sbostic         int m,n,i;
25231856Szliu #if defined(vax)||defined(tahoe)
25324581Szliu         int k=54;
25431856Szliu #else	/* defined(vax)||defined(tahoe) */
25524581Szliu         int k=51;
25631856Szliu #endif	/* defined(vax)||defined(tahoe) */
25724581Szliu 
25824581Szliu     /* sqrt(NaN) is NaN, sqrt(+-0) = +-0 */
25924581Szliu         if(x!=x||x==zero) return(x);
26024581Szliu 
26124581Szliu     /* sqrt(negative) is invalid */
26231828Szliu         if(x<zero) {
26331856Szliu #if defined(vax)||defined(tahoe)
26431828Szliu 		return (infnan(EDOM));	/* NaN */
26531856Szliu #else	/* defined(vax)||defined(tahoe) */
26631828Szliu 		return(zero/zero);
26731856Szliu #endif	/* defined(vax)||defined(tahoe) */
26831828Szliu 	}
26924581Szliu 
27024581Szliu     /* sqrt(INF) is INF */
27124581Szliu         if(!finite(x)) return(x);
27224581Szliu 
27324581Szliu     /* scale x to [1,4) */
27424581Szliu         n=logb(x);
27524581Szliu         x=scalb(x,-n);
27624581Szliu         if((m=logb(x))!=0) x=scalb(x,-m);       /* subnormal number */
27724581Szliu         m += n;
27824581Szliu         n = m/2;
27924581Szliu         if((n+n)!=m) {x *= 2; m -=1; n=m/2;}
28024581Szliu 
28124581Szliu     /* generate sqrt(x) bit by bit (accumulating in q) */
28224581Szliu             q=1.0; s=4.0; x -= 1.0; r=1;
28324581Szliu             for(i=1;i<=k;i++) {
28424581Szliu                 t=s+1; x *= 4; r /= 2;
28524581Szliu                 if(t<=x) {
28624581Szliu                     s=t+t+2, x -= t; q += r;}
28724581Szliu                 else
28824581Szliu                     s *= 2;
28924581Szliu                 }
29024581Szliu 
29124581Szliu     /* generate the last bit and determine the final rounding */
29224581Szliu             r/=2; x *= 4;
29324581Szliu             if(x==zero) goto end; 100+r; /* trigger inexact flag */
29424581Szliu             if(s<x) {
29524581Szliu                 q+=r; x -=s; s += 2; s *= 2; x *= 4;
29624581Szliu                 t = (x-s)-5;
29724581Szliu                 b=1.0+3*r/4; if(b==1.0) goto end; /* b==1 : Round-to-zero */
29824581Szliu                 b=1.0+r/4;   if(b>1.0) t=1;	/* b>1 : Round-to-(+INF) */
29924581Szliu                 if(t>=0) q+=r; }	      /* else: Round-to-nearest */
30024581Szliu             else {
30124581Szliu                 s *= 2; x *= 4;
30224581Szliu                 t = (x-s)-1;
30324581Szliu                 b=1.0+3*r/4; if(b==1.0) goto end;
30424581Szliu                 b=1.0+r/4;   if(b>1.0) t=1;
30524581Szliu                 if(t>=0) q+=r; }
30624581Szliu 
30724581Szliu end:        return(scalb(q,n));
30824581Szliu }
30924581Szliu 
31024581Szliu #if 0
31124581Szliu /* DREM(X,Y)
31224581Szliu  * RETURN X REM Y =X-N*Y, N=[X/Y] ROUNDED (ROUNDED TO EVEN IN THE HALF WAY CASE)
31324581Szliu  * DOUBLE PRECISION (VAX D format 56 bits, IEEE DOUBLE 53 BITS)
31424581Szliu  * INTENDED FOR ASSEMBLY LANGUAGE
31524581Szliu  * CODED IN C BY K.C. NG, 3/23/85, 4/8/85.
31624581Szliu  *
31724581Szliu  * Warning: this code should not get compiled in unless ALL of
31824581Szliu  * the following machine-dependent routines are supplied.
31924581Szliu  *
32024581Szliu  * Required machine dependent functions (not on a VAX):
32124581Szliu  *     swapINX(i): save inexact flag and reset it to "i"
32224581Szliu  *     swapENI(e): save inexact enable and reset it to "e"
32324581Szliu  */
32424581Szliu 
32524581Szliu double drem(x,y)
32624581Szliu double x,y;
32724581Szliu {
32824581Szliu 
32931856Szliu #ifdef national		/* order of words in floating point number */
33035681Sbostic 	static const n0=3,n1=2,n2=1,n3=0;
33131789Szliu #else /* VAX, SUN, ZILOG, TAHOE */
33235681Sbostic 	static const n0=0,n1=1,n2=2,n3=3;
33324581Szliu #endif
33424581Szliu 
33535681Sbostic     	static const unsigned short mexp =0x7ff0, m25 =0x0190, m57 =0x0390;
33635681Sbostic 	static const double zero=0.0;
33724581Szliu 	double hy,y1,t,t1;
33824581Szliu 	short k;
33924581Szliu 	long n;
34024581Szliu 	int i,e;
34124581Szliu 	unsigned short xexp,yexp, *px  =(unsigned short *) &x  ,
34224581Szliu 	      		nx,nf,	  *py  =(unsigned short *) &y  ,
34324581Szliu 	      		sign,	  *pt  =(unsigned short *) &t  ,
34424581Szliu 	      			  *pt1 =(unsigned short *) &t1 ;
34524581Szliu 
34624581Szliu 	xexp = px[n0] & mexp ;	/* exponent of x */
34724581Szliu 	yexp = py[n0] & mexp ;	/* exponent of y */
34824581Szliu 	sign = px[n0] &0x8000;	/* sign of x     */
34924581Szliu 
35024581Szliu /* return NaN if x is NaN, or y is NaN, or x is INF, or y is zero */
35124581Szliu 	if(x!=x) return(x); if(y!=y) return(y);	     /* x or y is NaN */
35224581Szliu 	if( xexp == mexp )   return(zero/zero);      /* x is INF */
35324581Szliu 	if(y==zero) return(y/y);
35424581Szliu 
35524581Szliu /* save the inexact flag and inexact enable in i and e respectively
35624581Szliu  * and reset them to zero
35724581Szliu  */
35824581Szliu 	i=swapINX(0);	e=swapENI(0);
35924581Szliu 
36024581Szliu /* subnormal number */
36124581Szliu 	nx=0;
36224581Szliu 	if(yexp==0) {t=1.0,pt[n0]+=m57; y*=t; nx=m57;}
36324581Szliu 
36424581Szliu /* if y is tiny (biased exponent <= 57), scale up y to y*2**57 */
36524581Szliu 	if( yexp <= m57 ) {py[n0]+=m57; nx+=m57; yexp+=m57;}
36624581Szliu 
36724581Szliu 	nf=nx;
36824581Szliu 	py[n0] &= 0x7fff;
36924581Szliu 	px[n0] &= 0x7fff;
37024581Szliu 
37124581Szliu /* mask off the least significant 27 bits of y */
37224581Szliu 	t=y; pt[n3]=0; pt[n2]&=0xf800; y1=t;
37324581Szliu 
37424581Szliu /* LOOP: argument reduction on x whenever x > y */
37524581Szliu loop:
37624581Szliu 	while ( x > y )
37724581Szliu 	{
37824581Szliu 	    t=y;
37924581Szliu 	    t1=y1;
38024581Szliu 	    xexp=px[n0]&mexp;	  /* exponent of x */
38124581Szliu 	    k=xexp-yexp-m25;
38224581Szliu 	    if(k>0) 	/* if x/y >= 2**26, scale up y so that x/y < 2**26 */
38324581Szliu 		{pt[n0]+=k;pt1[n0]+=k;}
38424581Szliu 	    n=x/t; x=(x-n*t1)-n*(t-t1);
38524581Szliu 	}
38624581Szliu     /* end while (x > y) */
38724581Szliu 
38824581Szliu 	if(nx!=0) {t=1.0; pt[n0]+=nx; x*=t; nx=0; goto loop;}
38924581Szliu 
39024581Szliu /* final adjustment */
39124581Szliu 
39224581Szliu 	hy=y/2.0;
39324581Szliu 	if(x>hy||((x==hy)&&n%2==1)) x-=y;
39424581Szliu 	px[n0] ^= sign;
39524581Szliu 	if(nf!=0) { t=1.0; pt[n0]-=nf; x*=t;}
39624581Szliu 
39724581Szliu /* restore inexact flag and inexact enable */
39824581Szliu 	swapINX(i); swapENI(e);
39924581Szliu 
40024581Szliu 	return(x);
40124581Szliu }
40224581Szliu #endif
40324581Szliu 
40424581Szliu #if 0
40524581Szliu /* SQRT
40624581Szliu  * RETURN CORRECTLY ROUNDED (ACCORDING TO THE ROUNDING MODE) SQRT
40724581Szliu  * FOR IEEE DOUBLE PRECISION ONLY, INTENDED FOR ASSEMBLY LANGUAGE
40824581Szliu  * CODED IN C BY K.C. NG, 3/22/85.
40924581Szliu  *
41024581Szliu  * Warning: this code should not get compiled in unless ALL of
41124581Szliu  * the following machine-dependent routines are supplied.
41224581Szliu  *
41324581Szliu  * Required machine dependent functions:
41424581Szliu  *     swapINX(i)  ...return the status of INEXACT flag and reset it to "i"
41524581Szliu  *     swapRM(r)   ...return the current Rounding Mode and reset it to "r"
41624581Szliu  *     swapENI(e)  ...return the status of inexact enable and reset it to "e"
41724581Szliu  *     addc(t)     ...perform t=t+1 regarding t as a 64 bit unsigned integer
41824581Szliu  *     subc(t)     ...perform t=t-1 regarding t as a 64 bit unsigned integer
41924581Szliu  */
42024581Szliu 
42135681Sbostic static const unsigned long table[] = {
42224581Szliu 0, 1204, 3062, 5746, 9193, 13348, 18162, 23592, 29598, 36145, 43202, 50740,
42324581Szliu 58733, 67158, 75992, 85215, 83599, 71378, 60428, 50647, 41945, 34246, 27478,
42424581Szliu 21581, 16499, 12183, 8588, 5674, 3403, 1742, 661, 130, };
42524581Szliu 
42624581Szliu double newsqrt(x)
42724581Szliu double x;
42824581Szliu {
42935681Sbostic         double y,z,t,addc(),subc()
43035681Sbostic 	double const b54=134217728.*134217728.; /* b54=2**54 */
43135681Sbostic         long mx,scalx;
43235681Sbostic 	long const mexp=0x7ff00000;
43324581Szliu         int i,j,r,e,swapINX(),swapRM(),swapENI();
43424581Szliu         unsigned long *py=(unsigned long *) &y   ,
43524581Szliu                       *pt=(unsigned long *) &t   ,
43624581Szliu                       *px=(unsigned long *) &x   ;
43731856Szliu #ifdef national         /* ordering of word in a floating point number */
43835681Sbostic         const int n0=1, n1=0;
43924581Szliu #else
44035681Sbostic         const int n0=0, n1=1;
44124581Szliu #endif
44224581Szliu /* Rounding Mode:  RN ...round-to-nearest
44324581Szliu  *                 RZ ...round-towards 0
44424581Szliu  *                 RP ...round-towards +INF
44524581Szliu  *		   RM ...round-towards -INF
44624581Szliu  */
44735681Sbostic         const int RN=0,RZ=1,RP=2,RM=3;
44835681Sbostic 				/* machine dependent: work on a Zilog Z8070
44924581Szliu                                  * and a National 32081 & 16081
45024581Szliu                                  */
45124581Szliu 
45224581Szliu /* exceptions */
45324581Szliu 	if(x!=x||x==0.0) return(x);  /* sqrt(NaN) is NaN, sqrt(+-0) = +-0 */
45424581Szliu 	if(x<0) return((x-x)/(x-x)); /* sqrt(negative) is invalid */
45524581Szliu         if((mx=px[n0]&mexp)==mexp) return(x);  /* sqrt(+INF) is +INF */
45624581Szliu 
45724581Szliu /* save, reset, initialize */
45824581Szliu         e=swapENI(0);   /* ...save and reset the inexact enable */
45924581Szliu         i=swapINX(0);   /* ...save INEXACT flag */
46024581Szliu         r=swapRM(RN);   /* ...save and reset the Rounding Mode to RN */
46124581Szliu         scalx=0;
46224581Szliu 
46324581Szliu /* subnormal number, scale up x to x*2**54 */
46424581Szliu         if(mx==0) {x *= b54 ; scalx-=0x01b00000;}
46524581Szliu 
46624581Szliu /* scale x to avoid intermediate over/underflow:
46724581Szliu  * if (x > 2**512) x=x/2**512; if (x < 2**-512) x=x*2**512 */
46824581Szliu         if(mx>0x5ff00000) {px[n0] -= 0x20000000; scalx+= 0x10000000;}
46924581Szliu         if(mx<0x1ff00000) {px[n0] += 0x20000000; scalx-= 0x10000000;}
47024581Szliu 
47124581Szliu /* magic initial approximation to almost 8 sig. bits */
47224581Szliu         py[n0]=(px[n0]>>1)+0x1ff80000;
47324581Szliu         py[n0]=py[n0]-table[(py[n0]>>15)&31];
47424581Szliu 
47524581Szliu /* Heron's rule once with correction to improve y to almost 18 sig. bits */
47624581Szliu         t=x/y; y=y+t; py[n0]=py[n0]-0x00100006; py[n1]=0;
47724581Szliu 
47824581Szliu /* triple to almost 56 sig. bits; now y approx. sqrt(x) to within 1 ulp */
47924581Szliu         t=y*y; z=t;  pt[n0]+=0x00100000; t+=z; z=(x-z)*y;
48024581Szliu         t=z/(t+x) ;  pt[n0]+=0x00100000; y+=t;
48124581Szliu 
48224581Szliu /* twiddle last bit to force y correctly rounded */
48324581Szliu         swapRM(RZ);     /* ...set Rounding Mode to round-toward-zero */
48424581Szliu         swapINX(0);     /* ...clear INEXACT flag */
48524581Szliu         swapENI(e);     /* ...restore inexact enable status */
48624581Szliu         t=x/y;          /* ...chopped quotient, possibly inexact */
48724581Szliu         j=swapINX(i);   /* ...read and restore inexact flag */
48824581Szliu         if(j==0) { if(t==y) goto end; else t=subc(t); }  /* ...t=t-ulp */
48924581Szliu         b54+0.1;        /* ..trigger inexact flag, sqrt(x) is inexact */
49024581Szliu         if(r==RN) t=addc(t);            /* ...t=t+ulp */
49124581Szliu         else if(r==RP) { t=addc(t);y=addc(y);}/* ...t=t+ulp;y=y+ulp; */
49224581Szliu         y=y+t;                          /* ...chopped sum */
49324581Szliu         py[n0]=py[n0]-0x00100000;       /* ...correctly rounded sqrt(x) */
49424581Szliu end:    py[n0]=py[n0]+scalx;            /* ...scale back y */
49524581Szliu         swapRM(r);                      /* ...restore Rounding Mode */
49624581Szliu         return(y);
49724581Szliu }
49824581Szliu #endif
499