xref: /csrg-svn/lib/libm/ieee/support.c (revision 34123)
1*34123Sbostic /*
224581Szliu  * Copyright (c) 1985 Regents of the University of California.
3*34123Sbostic  * All rights reserved.
4*34123Sbostic  *
5*34123Sbostic  * Redistribution and use in source and binary forms are permitted
6*34123Sbostic  * provided that this notice is preserved and that due credit is given
7*34123Sbostic  * to the University of California at Berkeley. The name of the University
8*34123Sbostic  * may not be used to endorse or promote products derived from this
9*34123Sbostic  * software without specific prior written permission. This software
10*34123Sbostic  * is provided ``as is'' without express or implied warranty.
11*34123Sbostic  *
12*34123Sbostic  * All recipients should regard themselves as participants in an ongoing
13*34123Sbostic  * research project and hence should feel obligated to report their
14*34123Sbostic  * experiences (good or bad) with these elementary function codes, using
15*34123Sbostic  * the sendbug(8) program, to the authors.
1624581Szliu  */
1724581Szliu 
1824581Szliu #ifndef lint
19*34123Sbostic static char sccsid[] = "@(#)support.c	5.2 (Berkeley) 04/29/88";
20*34123Sbostic #endif /* not lint */
2124581Szliu 
2224581Szliu /*
2331828Szliu  * Some IEEE standard 754 recommended functions and remainder and sqrt for
2424581Szliu  * supporting the C elementary functions.
2524581Szliu  ******************************************************************************
2624581Szliu  * WARNING:
2724581Szliu  *      These codes are developed (in double) to support the C elementary
2824581Szliu  * functions temporarily. They are not universal, and some of them are very
2924581Szliu  * slow (in particular, drem and sqrt is extremely inefficient). Each
3024581Szliu  * computer system should have its implementation of these functions using
3124581Szliu  * its own assembler.
3224581Szliu  ******************************************************************************
3324581Szliu  *
3431828Szliu  * IEEE 754 required operations:
3524581Szliu  *     drem(x,p)
3624581Szliu  *              returns  x REM y  =  x - [x/y]*y , where [x/y] is the integer
3724581Szliu  *              nearest x/y; in half way case, choose the even one.
3824581Szliu  *     sqrt(x)
3924581Szliu  *              returns the square root of x correctly rounded according to
4024581Szliu  *		the rounding mod.
4124581Szliu  *
4231828Szliu  * IEEE 754 recommended functions:
4324581Szliu  * (a) copysign(x,y)
4424581Szliu  *              returns x with the sign of y.
4524581Szliu  * (b) scalb(x,N)
4624581Szliu  *              returns  x * (2**N), for integer values N.
4724581Szliu  * (c) logb(x)
4824581Szliu  *              returns the unbiased exponent of x, a signed integer in
4924581Szliu  *              double precision, except that logb(0) is -INF, logb(INF)
5024581Szliu  *              is +INF, and logb(NAN) is that NAN.
5124581Szliu  * (d) finite(x)
5224581Szliu  *              returns the value TRUE if -INF < x < +INF and returns
5324581Szliu  *              FALSE otherwise.
5424581Szliu  *
5524581Szliu  *
5624581Szliu  * CODED IN C BY K.C. NG, 11/25/84;
5724581Szliu  * REVISED BY K.C. NG on 1/22/85, 2/13/85, 3/24/85.
5824581Szliu  */
5924581Szliu 
6024581Szliu 
6131856Szliu #if defined(vax)||defined(tahoe)      /* VAX D format */
6231828Szliu #include <errno.h>
6324581Szliu     static unsigned short msign=0x7fff , mexp =0x7f80 ;
6424581Szliu     static short  prep1=57, gap=7, bias=129           ;
6524581Szliu     static double novf=1.7E38, nunf=3.0E-39, zero=0.0 ;
6631856Szliu #else	/* defined(vax)||defined(tahoe) */
6724581Szliu     static unsigned short msign=0x7fff, mexp =0x7ff0  ;
6824581Szliu     static short prep1=54, gap=4, bias=1023           ;
6924581Szliu     static double novf=1.7E308, nunf=3.0E-308,zero=0.0;
7031856Szliu #endif	/* defined(vax)||defined(tahoe) */
7124581Szliu 
7224581Szliu double scalb(x,N)
7324581Szliu double x; int N;
7424581Szliu {
7524581Szliu         int k;
7624581Szliu         double scalb();
7724581Szliu 
7831856Szliu #ifdef national
7924581Szliu         unsigned short *px=(unsigned short *) &x + 3;
8031856Szliu #else	/* national */
8124581Szliu         unsigned short *px=(unsigned short *) &x;
8231856Szliu #endif	/* national */
8324581Szliu 
8424581Szliu         if( x == zero )  return(x);
8524581Szliu 
8631856Szliu #if defined(vax)||defined(tahoe)
8724581Szliu         if( (k= *px & mexp ) != ~msign ) {
8831828Szliu             if (N < -260)
8931828Szliu 		return(nunf*nunf);
9031828Szliu 	    else if (N > 260) {
9131828Szliu 		extern double infnan(),copysign();
9231828Szliu 		return(copysign(infnan(ERANGE),x));
9331828Szliu 	    }
9431856Szliu #else	/* defined(vax)||defined(tahoe) */
9524581Szliu         if( (k= *px & mexp ) != mexp ) {
9624581Szliu             if( N<-2100) return(nunf*nunf); else if(N>2100) return(novf+novf);
9724581Szliu             if( k == 0 ) {
9824581Szliu                  x *= scalb(1.0,(int)prep1);  N -= prep1; return(scalb(x,N));}
9931856Szliu #endif	/* defined(vax)||defined(tahoe) */
10024581Szliu 
10124581Szliu             if((k = (k>>gap)+ N) > 0 )
10224581Szliu                 if( k < (mexp>>gap) ) *px = (*px&~mexp) | (k<<gap);
10324581Szliu                 else x=novf+novf;               /* overflow */
10424581Szliu             else
10524581Szliu                 if( k > -prep1 )
10624581Szliu                                         /* gradual underflow */
10724581Szliu                     {*px=(*px&~mexp)|(short)(1<<gap); x *= scalb(1.0,k-1);}
10824581Szliu                 else
10924581Szliu                 return(nunf*nunf);
11024581Szliu             }
11124581Szliu         return(x);
11224581Szliu }
11324581Szliu 
11424581Szliu 
11524581Szliu double copysign(x,y)
11624581Szliu double x,y;
11724581Szliu {
11831856Szliu #ifdef national
11924581Szliu         unsigned short  *px=(unsigned short *) &x+3,
12024581Szliu                         *py=(unsigned short *) &y+3;
12131856Szliu #else	/* national */
12224581Szliu         unsigned short  *px=(unsigned short *) &x,
12324581Szliu                         *py=(unsigned short *) &y;
12431856Szliu #endif	/* national */
12524581Szliu 
12631856Szliu #if defined(vax)||defined(tahoe)
12724581Szliu         if ( (*px & mexp) == 0 ) return(x);
12831856Szliu #endif	/* defined(vax)||defined(tahoe) */
12924581Szliu 
13024581Szliu         *px = ( *px & msign ) | ( *py & ~msign );
13124581Szliu         return(x);
13224581Szliu }
13324581Szliu 
13424581Szliu double logb(x)
13524581Szliu double x;
13624581Szliu {
13724581Szliu 
13831856Szliu #ifdef national
13924581Szliu         short *px=(short *) &x+3, k;
14031856Szliu #else	/* national */
14124581Szliu         short *px=(short *) &x, k;
14231856Szliu #endif	/* national */
14324581Szliu 
14431856Szliu #if defined(vax)||defined(tahoe)
14527449Szliu         return (int)(((*px&mexp)>>gap)-bias);
14631856Szliu #else	/* defined(vax)||defined(tahoe) */
14724581Szliu         if( (k= *px & mexp ) != mexp )
14824581Szliu             if ( k != 0 )
14924581Szliu                 return ( (k>>gap) - bias );
15024581Szliu             else if( x != zero)
15124581Szliu                 return ( -1022.0 );
15224581Szliu             else
15324581Szliu                 return(-(1.0/zero));
15424581Szliu         else if(x != x)
15524581Szliu             return(x);
15624581Szliu         else
15724581Szliu             {*px &= msign; return(x);}
15831856Szliu #endif	/* defined(vax)||defined(tahoe) */
15924581Szliu }
16024581Szliu 
16124581Szliu finite(x)
16224581Szliu double x;
16324581Szliu {
16431856Szliu #if defined(vax)||defined(tahoe)
16531828Szliu         return(1);
16631856Szliu #else	/* defined(vax)||defined(tahoe) */
16731856Szliu #ifdef national
16824581Szliu         return( (*((short *) &x+3 ) & mexp ) != mexp );
16931856Szliu #else	/* national */
17024581Szliu         return( (*((short *) &x ) & mexp ) != mexp );
17131856Szliu #endif	/* national */
17231856Szliu #endif	/* defined(vax)||defined(tahoe) */
17324581Szliu }
17424581Szliu 
17524581Szliu double drem(x,p)
17624581Szliu double x,p;
17724581Szliu {
17824581Szliu         short sign;
17924581Szliu         double hp,dp,tmp,drem(),scalb();
18024581Szliu         unsigned short  k;
18131856Szliu #ifdef national
18224581Szliu         unsigned short
18324581Szliu               *px=(unsigned short *) &x  +3,
18424581Szliu               *pp=(unsigned short *) &p  +3,
18524581Szliu               *pd=(unsigned short *) &dp +3,
18624581Szliu               *pt=(unsigned short *) &tmp+3;
18731856Szliu #else	/* national */
18824581Szliu         unsigned short
18924581Szliu               *px=(unsigned short *) &x  ,
19024581Szliu               *pp=(unsigned short *) &p  ,
19124581Szliu               *pd=(unsigned short *) &dp ,
19224581Szliu               *pt=(unsigned short *) &tmp;
19331856Szliu #endif	/* national */
19424581Szliu 
19524581Szliu         *pp &= msign ;
19624581Szliu 
19731856Szliu #if defined(vax)||defined(tahoe)
19831828Szliu         if( ( *px & mexp ) == ~msign )	/* is x a reserved operand? */
19931856Szliu #else	/* defined(vax)||defined(tahoe) */
20031827Szliu         if( ( *px & mexp ) == mexp )
20131856Szliu #endif	/* defined(vax)||defined(tahoe) */
20231827Szliu 		return  (x-p)-(x-p);	/* create nan if x is inf */
20331828Szliu 	if (p == zero) {
20431856Szliu #if defined(vax)||defined(tahoe)
20531828Szliu 		extern double infnan();
20631828Szliu 		return(infnan(EDOM));
20731856Szliu #else	/* defined(vax)||defined(tahoe) */
20831828Szliu 		return zero/zero;
20931856Szliu #endif	/* defined(vax)||defined(tahoe) */
21031828Szliu 	}
21131828Szliu 
21231856Szliu #if defined(vax)||defined(tahoe)
21331828Szliu         if( ( *pp & mexp ) == ~msign )	/* is p a reserved operand? */
21431856Szliu #else	/* defined(vax)||defined(tahoe) */
21531827Szliu         if( ( *pp & mexp ) == mexp )
21631856Szliu #endif	/* defined(vax)||defined(tahoe) */
21731827Szliu 		{ if (p != p) return p; else return x;}
21824581Szliu 
21924581Szliu         else  if ( ((*pp & mexp)>>gap) <= 1 )
22024581Szliu                 /* subnormal p, or almost subnormal p */
22124581Szliu             { double b; b=scalb(1.0,(int)prep1);
22224581Szliu               p *= b; x = drem(x,p); x *= b; return(drem(x,p)/b);}
22324581Szliu         else  if ( p >= novf/2)
22424581Szliu             { p /= 2 ; x /= 2; return(drem(x,p)*2);}
22524581Szliu         else
22624581Szliu             {
22724581Szliu                 dp=p+p; hp=p/2;
22824581Szliu                 sign= *px & ~msign ;
22924581Szliu                 *px &= msign       ;
23024581Szliu                 while ( x > dp )
23124581Szliu                     {
23224581Szliu                         k=(*px & mexp) - (*pd & mexp) ;
23324581Szliu                         tmp = dp ;
23424581Szliu                         *pt += k ;
23524581Szliu 
23631856Szliu #if defined(vax)||defined(tahoe)
23724581Szliu                         if( x < tmp ) *pt -= 128 ;
23831856Szliu #else	/* defined(vax)||defined(tahoe) */
23924581Szliu                         if( x < tmp ) *pt -= 16 ;
24031856Szliu #endif	/* defined(vax)||defined(tahoe) */
24124581Szliu 
24224581Szliu                         x -= tmp ;
24324581Szliu                     }
24424581Szliu                 if ( x > hp )
24524581Szliu                     { x -= p ;  if ( x >= hp ) x -= p ; }
24624581Szliu 
24731856Szliu #if defined(vax)||defined(tahoe)
24831825Szliu 		if (x)
24931856Szliu #endif	/* defined(vax)||defined(tahoe) */
25031825Szliu 			*px ^= sign;
25124581Szliu                 return( x);
25224581Szliu 
25324581Szliu             }
25424581Szliu }
25524581Szliu double sqrt(x)
25624581Szliu double x;
25724581Szliu {
25824581Szliu         double q,s,b,r;
25924581Szliu         double logb(),scalb();
26024581Szliu         double t,zero=0.0;
26124581Szliu         int m,n,i,finite();
26231856Szliu #if defined(vax)||defined(tahoe)
26324581Szliu         int k=54;
26431856Szliu #else	/* defined(vax)||defined(tahoe) */
26524581Szliu         int k=51;
26631856Szliu #endif	/* defined(vax)||defined(tahoe) */
26724581Szliu 
26824581Szliu     /* sqrt(NaN) is NaN, sqrt(+-0) = +-0 */
26924581Szliu         if(x!=x||x==zero) return(x);
27024581Szliu 
27124581Szliu     /* sqrt(negative) is invalid */
27231828Szliu         if(x<zero) {
27331856Szliu #if defined(vax)||defined(tahoe)
27431828Szliu 		extern double infnan();
27531828Szliu 		return (infnan(EDOM));	/* NaN */
27631856Szliu #else	/* defined(vax)||defined(tahoe) */
27731828Szliu 		return(zero/zero);
27831856Szliu #endif	/* defined(vax)||defined(tahoe) */
27931828Szliu 	}
28024581Szliu 
28124581Szliu     /* sqrt(INF) is INF */
28224581Szliu         if(!finite(x)) return(x);
28324581Szliu 
28424581Szliu     /* scale x to [1,4) */
28524581Szliu         n=logb(x);
28624581Szliu         x=scalb(x,-n);
28724581Szliu         if((m=logb(x))!=0) x=scalb(x,-m);       /* subnormal number */
28824581Szliu         m += n;
28924581Szliu         n = m/2;
29024581Szliu         if((n+n)!=m) {x *= 2; m -=1; n=m/2;}
29124581Szliu 
29224581Szliu     /* generate sqrt(x) bit by bit (accumulating in q) */
29324581Szliu             q=1.0; s=4.0; x -= 1.0; r=1;
29424581Szliu             for(i=1;i<=k;i++) {
29524581Szliu                 t=s+1; x *= 4; r /= 2;
29624581Szliu                 if(t<=x) {
29724581Szliu                     s=t+t+2, x -= t; q += r;}
29824581Szliu                 else
29924581Szliu                     s *= 2;
30024581Szliu                 }
30124581Szliu 
30224581Szliu     /* generate the last bit and determine the final rounding */
30324581Szliu             r/=2; x *= 4;
30424581Szliu             if(x==zero) goto end; 100+r; /* trigger inexact flag */
30524581Szliu             if(s<x) {
30624581Szliu                 q+=r; x -=s; s += 2; s *= 2; x *= 4;
30724581Szliu                 t = (x-s)-5;
30824581Szliu                 b=1.0+3*r/4; if(b==1.0) goto end; /* b==1 : Round-to-zero */
30924581Szliu                 b=1.0+r/4;   if(b>1.0) t=1;	/* b>1 : Round-to-(+INF) */
31024581Szliu                 if(t>=0) q+=r; }	      /* else: Round-to-nearest */
31124581Szliu             else {
31224581Szliu                 s *= 2; x *= 4;
31324581Szliu                 t = (x-s)-1;
31424581Szliu                 b=1.0+3*r/4; if(b==1.0) goto end;
31524581Szliu                 b=1.0+r/4;   if(b>1.0) t=1;
31624581Szliu                 if(t>=0) q+=r; }
31724581Szliu 
31824581Szliu end:        return(scalb(q,n));
31924581Szliu }
32024581Szliu 
32124581Szliu #if 0
32224581Szliu /* DREM(X,Y)
32324581Szliu  * RETURN X REM Y =X-N*Y, N=[X/Y] ROUNDED (ROUNDED TO EVEN IN THE HALF WAY CASE)
32424581Szliu  * DOUBLE PRECISION (VAX D format 56 bits, IEEE DOUBLE 53 BITS)
32524581Szliu  * INTENDED FOR ASSEMBLY LANGUAGE
32624581Szliu  * CODED IN C BY K.C. NG, 3/23/85, 4/8/85.
32724581Szliu  *
32824581Szliu  * Warning: this code should not get compiled in unless ALL of
32924581Szliu  * the following machine-dependent routines are supplied.
33024581Szliu  *
33124581Szliu  * Required machine dependent functions (not on a VAX):
33224581Szliu  *     swapINX(i): save inexact flag and reset it to "i"
33324581Szliu  *     swapENI(e): save inexact enable and reset it to "e"
33424581Szliu  */
33524581Szliu 
33624581Szliu double drem(x,y)
33724581Szliu double x,y;
33824581Szliu {
33924581Szliu 
34031856Szliu #ifdef national		/* order of words in floating point number */
34124581Szliu 	static n0=3,n1=2,n2=1,n3=0;
34231789Szliu #else /* VAX, SUN, ZILOG, TAHOE */
34324581Szliu 	static n0=0,n1=1,n2=2,n3=3;
34424581Szliu #endif
34524581Szliu 
34624581Szliu     	static unsigned short mexp =0x7ff0, m25 =0x0190, m57 =0x0390;
34724581Szliu 	static double zero=0.0;
34824581Szliu 	double hy,y1,t,t1;
34924581Szliu 	short k;
35024581Szliu 	long n;
35124581Szliu 	int i,e;
35224581Szliu 	unsigned short xexp,yexp, *px  =(unsigned short *) &x  ,
35324581Szliu 	      		nx,nf,	  *py  =(unsigned short *) &y  ,
35424581Szliu 	      		sign,	  *pt  =(unsigned short *) &t  ,
35524581Szliu 	      			  *pt1 =(unsigned short *) &t1 ;
35624581Szliu 
35724581Szliu 	xexp = px[n0] & mexp ;	/* exponent of x */
35824581Szliu 	yexp = py[n0] & mexp ;	/* exponent of y */
35924581Szliu 	sign = px[n0] &0x8000;	/* sign of x     */
36024581Szliu 
36124581Szliu /* return NaN if x is NaN, or y is NaN, or x is INF, or y is zero */
36224581Szliu 	if(x!=x) return(x); if(y!=y) return(y);	     /* x or y is NaN */
36324581Szliu 	if( xexp == mexp )   return(zero/zero);      /* x is INF */
36424581Szliu 	if(y==zero) return(y/y);
36524581Szliu 
36624581Szliu /* save the inexact flag and inexact enable in i and e respectively
36724581Szliu  * and reset them to zero
36824581Szliu  */
36924581Szliu 	i=swapINX(0);	e=swapENI(0);
37024581Szliu 
37124581Szliu /* subnormal number */
37224581Szliu 	nx=0;
37324581Szliu 	if(yexp==0) {t=1.0,pt[n0]+=m57; y*=t; nx=m57;}
37424581Szliu 
37524581Szliu /* if y is tiny (biased exponent <= 57), scale up y to y*2**57 */
37624581Szliu 	if( yexp <= m57 ) {py[n0]+=m57; nx+=m57; yexp+=m57;}
37724581Szliu 
37824581Szliu 	nf=nx;
37924581Szliu 	py[n0] &= 0x7fff;
38024581Szliu 	px[n0] &= 0x7fff;
38124581Szliu 
38224581Szliu /* mask off the least significant 27 bits of y */
38324581Szliu 	t=y; pt[n3]=0; pt[n2]&=0xf800; y1=t;
38424581Szliu 
38524581Szliu /* LOOP: argument reduction on x whenever x > y */
38624581Szliu loop:
38724581Szliu 	while ( x > y )
38824581Szliu 	{
38924581Szliu 	    t=y;
39024581Szliu 	    t1=y1;
39124581Szliu 	    xexp=px[n0]&mexp;	  /* exponent of x */
39224581Szliu 	    k=xexp-yexp-m25;
39324581Szliu 	    if(k>0) 	/* if x/y >= 2**26, scale up y so that x/y < 2**26 */
39424581Szliu 		{pt[n0]+=k;pt1[n0]+=k;}
39524581Szliu 	    n=x/t; x=(x-n*t1)-n*(t-t1);
39624581Szliu 	}
39724581Szliu     /* end while (x > y) */
39824581Szliu 
39924581Szliu 	if(nx!=0) {t=1.0; pt[n0]+=nx; x*=t; nx=0; goto loop;}
40024581Szliu 
40124581Szliu /* final adjustment */
40224581Szliu 
40324581Szliu 	hy=y/2.0;
40424581Szliu 	if(x>hy||((x==hy)&&n%2==1)) x-=y;
40524581Szliu 	px[n0] ^= sign;
40624581Szliu 	if(nf!=0) { t=1.0; pt[n0]-=nf; x*=t;}
40724581Szliu 
40824581Szliu /* restore inexact flag and inexact enable */
40924581Szliu 	swapINX(i); swapENI(e);
41024581Szliu 
41124581Szliu 	return(x);
41224581Szliu }
41324581Szliu #endif
41424581Szliu 
41524581Szliu #if 0
41624581Szliu /* SQRT
41724581Szliu  * RETURN CORRECTLY ROUNDED (ACCORDING TO THE ROUNDING MODE) SQRT
41824581Szliu  * FOR IEEE DOUBLE PRECISION ONLY, INTENDED FOR ASSEMBLY LANGUAGE
41924581Szliu  * CODED IN C BY K.C. NG, 3/22/85.
42024581Szliu  *
42124581Szliu  * Warning: this code should not get compiled in unless ALL of
42224581Szliu  * the following machine-dependent routines are supplied.
42324581Szliu  *
42424581Szliu  * Required machine dependent functions:
42524581Szliu  *     swapINX(i)  ...return the status of INEXACT flag and reset it to "i"
42624581Szliu  *     swapRM(r)   ...return the current Rounding Mode and reset it to "r"
42724581Szliu  *     swapENI(e)  ...return the status of inexact enable and reset it to "e"
42824581Szliu  *     addc(t)     ...perform t=t+1 regarding t as a 64 bit unsigned integer
42924581Szliu  *     subc(t)     ...perform t=t-1 regarding t as a 64 bit unsigned integer
43024581Szliu  */
43124581Szliu 
43224581Szliu static unsigned long table[] = {
43324581Szliu 0, 1204, 3062, 5746, 9193, 13348, 18162, 23592, 29598, 36145, 43202, 50740,
43424581Szliu 58733, 67158, 75992, 85215, 83599, 71378, 60428, 50647, 41945, 34246, 27478,
43524581Szliu 21581, 16499, 12183, 8588, 5674, 3403, 1742, 661, 130, };
43624581Szliu 
43724581Szliu double newsqrt(x)
43824581Szliu double x;
43924581Szliu {
44024581Szliu         double y,z,t,addc(),subc(),b54=134217728.*134217728.; /* b54=2**54 */
44124581Szliu         long mx,scalx,mexp=0x7ff00000;
44224581Szliu         int i,j,r,e,swapINX(),swapRM(),swapENI();
44324581Szliu         unsigned long *py=(unsigned long *) &y   ,
44424581Szliu                       *pt=(unsigned long *) &t   ,
44524581Szliu                       *px=(unsigned long *) &x   ;
44631856Szliu #ifdef national         /* ordering of word in a floating point number */
44724581Szliu         int n0=1, n1=0;
44824581Szliu #else
44924581Szliu         int n0=0, n1=1;
45024581Szliu #endif
45124581Szliu /* Rounding Mode:  RN ...round-to-nearest
45224581Szliu  *                 RZ ...round-towards 0
45324581Szliu  *                 RP ...round-towards +INF
45424581Szliu  *		   RM ...round-towards -INF
45524581Szliu  */
45624581Szliu         int RN=0,RZ=1,RP=2,RM=3;/* machine dependent: work on a Zilog Z8070
45724581Szliu                                  * and a National 32081 & 16081
45824581Szliu                                  */
45924581Szliu 
46024581Szliu /* exceptions */
46124581Szliu 	if(x!=x||x==0.0) return(x);  /* sqrt(NaN) is NaN, sqrt(+-0) = +-0 */
46224581Szliu 	if(x<0) return((x-x)/(x-x)); /* sqrt(negative) is invalid */
46324581Szliu         if((mx=px[n0]&mexp)==mexp) return(x);  /* sqrt(+INF) is +INF */
46424581Szliu 
46524581Szliu /* save, reset, initialize */
46624581Szliu         e=swapENI(0);   /* ...save and reset the inexact enable */
46724581Szliu         i=swapINX(0);   /* ...save INEXACT flag */
46824581Szliu         r=swapRM(RN);   /* ...save and reset the Rounding Mode to RN */
46924581Szliu         scalx=0;
47024581Szliu 
47124581Szliu /* subnormal number, scale up x to x*2**54 */
47224581Szliu         if(mx==0) {x *= b54 ; scalx-=0x01b00000;}
47324581Szliu 
47424581Szliu /* scale x to avoid intermediate over/underflow:
47524581Szliu  * if (x > 2**512) x=x/2**512; if (x < 2**-512) x=x*2**512 */
47624581Szliu         if(mx>0x5ff00000) {px[n0] -= 0x20000000; scalx+= 0x10000000;}
47724581Szliu         if(mx<0x1ff00000) {px[n0] += 0x20000000; scalx-= 0x10000000;}
47824581Szliu 
47924581Szliu /* magic initial approximation to almost 8 sig. bits */
48024581Szliu         py[n0]=(px[n0]>>1)+0x1ff80000;
48124581Szliu         py[n0]=py[n0]-table[(py[n0]>>15)&31];
48224581Szliu 
48324581Szliu /* Heron's rule once with correction to improve y to almost 18 sig. bits */
48424581Szliu         t=x/y; y=y+t; py[n0]=py[n0]-0x00100006; py[n1]=0;
48524581Szliu 
48624581Szliu /* triple to almost 56 sig. bits; now y approx. sqrt(x) to within 1 ulp */
48724581Szliu         t=y*y; z=t;  pt[n0]+=0x00100000; t+=z; z=(x-z)*y;
48824581Szliu         t=z/(t+x) ;  pt[n0]+=0x00100000; y+=t;
48924581Szliu 
49024581Szliu /* twiddle last bit to force y correctly rounded */
49124581Szliu         swapRM(RZ);     /* ...set Rounding Mode to round-toward-zero */
49224581Szliu         swapINX(0);     /* ...clear INEXACT flag */
49324581Szliu         swapENI(e);     /* ...restore inexact enable status */
49424581Szliu         t=x/y;          /* ...chopped quotient, possibly inexact */
49524581Szliu         j=swapINX(i);   /* ...read and restore inexact flag */
49624581Szliu         if(j==0) { if(t==y) goto end; else t=subc(t); }  /* ...t=t-ulp */
49724581Szliu         b54+0.1;        /* ..trigger inexact flag, sqrt(x) is inexact */
49824581Szliu         if(r==RN) t=addc(t);            /* ...t=t+ulp */
49924581Szliu         else if(r==RP) { t=addc(t);y=addc(y);}/* ...t=t+ulp;y=y+ulp; */
50024581Szliu         y=y+t;                          /* ...chopped sum */
50124581Szliu         py[n0]=py[n0]-0x00100000;       /* ...correctly rounded sqrt(x) */
50224581Szliu end:    py[n0]=py[n0]+scalx;            /* ...scale back y */
50324581Szliu         swapRM(r);                      /* ...restore Rounding Mode */
50424581Szliu         return(y);
50524581Szliu }
50624581Szliu #endif
507