xref: /csrg-svn/lib/libm/ieee/support.c (revision 42657)
134123Sbostic /*
224581Szliu  * Copyright (c) 1985 Regents of the University of California.
334123Sbostic  * All rights reserved.
434123Sbostic  *
5*42657Sbostic  * %sccs.include.redist.c%
634123Sbostic  *
734123Sbostic  * All recipients should regard themselves as participants in an ongoing
834123Sbostic  * research project and hence should feel obligated to report their
934123Sbostic  * experiences (good or bad) with these elementary function codes, using
1034123Sbostic  * the sendbug(8) program, to the authors.
1124581Szliu  */
1224581Szliu 
1324581Szliu #ifndef lint
14*42657Sbostic static char sccsid[] = "@(#)support.c	5.5 (Berkeley) 06/01/90";
1534123Sbostic #endif /* not lint */
1624581Szliu 
1724581Szliu /*
1831828Szliu  * Some IEEE standard 754 recommended functions and remainder and sqrt for
1924581Szliu  * supporting the C elementary functions.
2024581Szliu  ******************************************************************************
2124581Szliu  * WARNING:
2224581Szliu  *      These codes are developed (in double) to support the C elementary
2324581Szliu  * functions temporarily. They are not universal, and some of them are very
2424581Szliu  * slow (in particular, drem and sqrt is extremely inefficient). Each
2524581Szliu  * computer system should have its implementation of these functions using
2624581Szliu  * its own assembler.
2724581Szliu  ******************************************************************************
2824581Szliu  *
2931828Szliu  * IEEE 754 required operations:
3024581Szliu  *     drem(x,p)
3124581Szliu  *              returns  x REM y  =  x - [x/y]*y , where [x/y] is the integer
3224581Szliu  *              nearest x/y; in half way case, choose the even one.
3324581Szliu  *     sqrt(x)
3424581Szliu  *              returns the square root of x correctly rounded according to
3524581Szliu  *		the rounding mod.
3624581Szliu  *
3731828Szliu  * IEEE 754 recommended functions:
3824581Szliu  * (a) copysign(x,y)
3924581Szliu  *              returns x with the sign of y.
4024581Szliu  * (b) scalb(x,N)
4124581Szliu  *              returns  x * (2**N), for integer values N.
4224581Szliu  * (c) logb(x)
4324581Szliu  *              returns the unbiased exponent of x, a signed integer in
4424581Szliu  *              double precision, except that logb(0) is -INF, logb(INF)
4524581Szliu  *              is +INF, and logb(NAN) is that NAN.
4624581Szliu  * (d) finite(x)
4724581Szliu  *              returns the value TRUE if -INF < x < +INF and returns
4824581Szliu  *              FALSE otherwise.
4924581Szliu  *
5024581Szliu  *
5124581Szliu  * CODED IN C BY K.C. NG, 11/25/84;
5224581Szliu  * REVISED BY K.C. NG on 1/22/85, 2/13/85, 3/24/85.
5324581Szliu  */
5424581Szliu 
5535681Sbostic #include "mathimpl.h"
5624581Szliu 
5731856Szliu #if defined(vax)||defined(tahoe)      /* VAX D format */
5831828Szliu #include <errno.h>
5935681Sbostic     static const unsigned short msign=0x7fff , mexp =0x7f80 ;
6035681Sbostic     static const short  prep1=57, gap=7, bias=129           ;
6135681Sbostic     static const double novf=1.7E38, nunf=3.0E-39, zero=0.0 ;
6231856Szliu #else	/* defined(vax)||defined(tahoe) */
6335681Sbostic     static const unsigned short msign=0x7fff, mexp =0x7ff0  ;
6435681Sbostic     static const short prep1=54, gap=4, bias=1023           ;
6535681Sbostic     static const double novf=1.7E308, nunf=3.0E-308,zero=0.0;
6631856Szliu #endif	/* defined(vax)||defined(tahoe) */
6724581Szliu 
6824581Szliu double scalb(x,N)
6924581Szliu double x; int N;
7024581Szliu {
7124581Szliu         int k;
7224581Szliu 
7331856Szliu #ifdef national
7424581Szliu         unsigned short *px=(unsigned short *) &x + 3;
7531856Szliu #else	/* national */
7624581Szliu         unsigned short *px=(unsigned short *) &x;
7731856Szliu #endif	/* national */
7824581Szliu 
7924581Szliu         if( x == zero )  return(x);
8024581Szliu 
8131856Szliu #if defined(vax)||defined(tahoe)
8224581Szliu         if( (k= *px & mexp ) != ~msign ) {
8331828Szliu             if (N < -260)
8431828Szliu 		return(nunf*nunf);
8531828Szliu 	    else if (N > 260) {
8631828Szliu 		return(copysign(infnan(ERANGE),x));
8731828Szliu 	    }
8831856Szliu #else	/* defined(vax)||defined(tahoe) */
8924581Szliu         if( (k= *px & mexp ) != mexp ) {
9024581Szliu             if( N<-2100) return(nunf*nunf); else if(N>2100) return(novf+novf);
9124581Szliu             if( k == 0 ) {
9224581Szliu                  x *= scalb(1.0,(int)prep1);  N -= prep1; return(scalb(x,N));}
9331856Szliu #endif	/* defined(vax)||defined(tahoe) */
9424581Szliu 
9524581Szliu             if((k = (k>>gap)+ N) > 0 )
9624581Szliu                 if( k < (mexp>>gap) ) *px = (*px&~mexp) | (k<<gap);
9724581Szliu                 else x=novf+novf;               /* overflow */
9824581Szliu             else
9924581Szliu                 if( k > -prep1 )
10024581Szliu                                         /* gradual underflow */
10124581Szliu                     {*px=(*px&~mexp)|(short)(1<<gap); x *= scalb(1.0,k-1);}
10224581Szliu                 else
10324581Szliu                 return(nunf*nunf);
10424581Szliu             }
10524581Szliu         return(x);
10624581Szliu }
10724581Szliu 
10824581Szliu 
10924581Szliu double copysign(x,y)
11024581Szliu double x,y;
11124581Szliu {
11231856Szliu #ifdef national
11324581Szliu         unsigned short  *px=(unsigned short *) &x+3,
11424581Szliu                         *py=(unsigned short *) &y+3;
11531856Szliu #else	/* national */
11624581Szliu         unsigned short  *px=(unsigned short *) &x,
11724581Szliu                         *py=(unsigned short *) &y;
11831856Szliu #endif	/* national */
11924581Szliu 
12031856Szliu #if defined(vax)||defined(tahoe)
12124581Szliu         if ( (*px & mexp) == 0 ) return(x);
12231856Szliu #endif	/* defined(vax)||defined(tahoe) */
12324581Szliu 
12424581Szliu         *px = ( *px & msign ) | ( *py & ~msign );
12524581Szliu         return(x);
12624581Szliu }
12724581Szliu 
12824581Szliu double logb(x)
12924581Szliu double x;
13024581Szliu {
13124581Szliu 
13231856Szliu #ifdef national
13324581Szliu         short *px=(short *) &x+3, k;
13431856Szliu #else	/* national */
13524581Szliu         short *px=(short *) &x, k;
13631856Szliu #endif	/* national */
13724581Szliu 
13831856Szliu #if defined(vax)||defined(tahoe)
13927449Szliu         return (int)(((*px&mexp)>>gap)-bias);
14031856Szliu #else	/* defined(vax)||defined(tahoe) */
14124581Szliu         if( (k= *px & mexp ) != mexp )
14224581Szliu             if ( k != 0 )
14324581Szliu                 return ( (k>>gap) - bias );
14424581Szliu             else if( x != zero)
14524581Szliu                 return ( -1022.0 );
14624581Szliu             else
14724581Szliu                 return(-(1.0/zero));
14824581Szliu         else if(x != x)
14924581Szliu             return(x);
15024581Szliu         else
15124581Szliu             {*px &= msign; return(x);}
15231856Szliu #endif	/* defined(vax)||defined(tahoe) */
15324581Szliu }
15424581Szliu 
15524581Szliu finite(x)
15624581Szliu double x;
15724581Szliu {
15831856Szliu #if defined(vax)||defined(tahoe)
15931828Szliu         return(1);
16031856Szliu #else	/* defined(vax)||defined(tahoe) */
16131856Szliu #ifdef national
16224581Szliu         return( (*((short *) &x+3 ) & mexp ) != mexp );
16331856Szliu #else	/* national */
16424581Szliu         return( (*((short *) &x ) & mexp ) != mexp );
16531856Szliu #endif	/* national */
16631856Szliu #endif	/* defined(vax)||defined(tahoe) */
16724581Szliu }
16824581Szliu 
16924581Szliu double drem(x,p)
17024581Szliu double x,p;
17124581Szliu {
17224581Szliu         short sign;
17335681Sbostic         double hp,dp,tmp;
17424581Szliu         unsigned short  k;
17531856Szliu #ifdef national
17624581Szliu         unsigned short
17724581Szliu               *px=(unsigned short *) &x  +3,
17824581Szliu               *pp=(unsigned short *) &p  +3,
17924581Szliu               *pd=(unsigned short *) &dp +3,
18024581Szliu               *pt=(unsigned short *) &tmp+3;
18131856Szliu #else	/* national */
18224581Szliu         unsigned short
18324581Szliu               *px=(unsigned short *) &x  ,
18424581Szliu               *pp=(unsigned short *) &p  ,
18524581Szliu               *pd=(unsigned short *) &dp ,
18624581Szliu               *pt=(unsigned short *) &tmp;
18731856Szliu #endif	/* national */
18824581Szliu 
18924581Szliu         *pp &= msign ;
19024581Szliu 
19131856Szliu #if defined(vax)||defined(tahoe)
19231828Szliu         if( ( *px & mexp ) == ~msign )	/* is x a reserved operand? */
19331856Szliu #else	/* defined(vax)||defined(tahoe) */
19431827Szliu         if( ( *px & mexp ) == mexp )
19531856Szliu #endif	/* defined(vax)||defined(tahoe) */
19631827Szliu 		return  (x-p)-(x-p);	/* create nan if x is inf */
19731828Szliu 	if (p == zero) {
19831856Szliu #if defined(vax)||defined(tahoe)
19931828Szliu 		return(infnan(EDOM));
20031856Szliu #else	/* defined(vax)||defined(tahoe) */
20131828Szliu 		return zero/zero;
20231856Szliu #endif	/* defined(vax)||defined(tahoe) */
20331828Szliu 	}
20431828Szliu 
20531856Szliu #if defined(vax)||defined(tahoe)
20631828Szliu         if( ( *pp & mexp ) == ~msign )	/* is p a reserved operand? */
20731856Szliu #else	/* defined(vax)||defined(tahoe) */
20831827Szliu         if( ( *pp & mexp ) == mexp )
20931856Szliu #endif	/* defined(vax)||defined(tahoe) */
21031827Szliu 		{ if (p != p) return p; else return x;}
21124581Szliu 
21224581Szliu         else  if ( ((*pp & mexp)>>gap) <= 1 )
21324581Szliu                 /* subnormal p, or almost subnormal p */
21424581Szliu             { double b; b=scalb(1.0,(int)prep1);
21524581Szliu               p *= b; x = drem(x,p); x *= b; return(drem(x,p)/b);}
21624581Szliu         else  if ( p >= novf/2)
21724581Szliu             { p /= 2 ; x /= 2; return(drem(x,p)*2);}
21824581Szliu         else
21924581Szliu             {
22024581Szliu                 dp=p+p; hp=p/2;
22124581Szliu                 sign= *px & ~msign ;
22224581Szliu                 *px &= msign       ;
22324581Szliu                 while ( x > dp )
22424581Szliu                     {
22524581Szliu                         k=(*px & mexp) - (*pd & mexp) ;
22624581Szliu                         tmp = dp ;
22724581Szliu                         *pt += k ;
22824581Szliu 
22931856Szliu #if defined(vax)||defined(tahoe)
23024581Szliu                         if( x < tmp ) *pt -= 128 ;
23131856Szliu #else	/* defined(vax)||defined(tahoe) */
23224581Szliu                         if( x < tmp ) *pt -= 16 ;
23331856Szliu #endif	/* defined(vax)||defined(tahoe) */
23424581Szliu 
23524581Szliu                         x -= tmp ;
23624581Szliu                     }
23724581Szliu                 if ( x > hp )
23824581Szliu                     { x -= p ;  if ( x >= hp ) x -= p ; }
23924581Szliu 
24031856Szliu #if defined(vax)||defined(tahoe)
24131825Szliu 		if (x)
24231856Szliu #endif	/* defined(vax)||defined(tahoe) */
24331825Szliu 			*px ^= sign;
24424581Szliu                 return( x);
24524581Szliu 
24624581Szliu             }
24724581Szliu }
24835681Sbostic 
24935681Sbostic 
25024581Szliu double sqrt(x)
25124581Szliu double x;
25224581Szliu {
25324581Szliu         double q,s,b,r;
25435681Sbostic         double t;
25535681Sbostic 	double const zero=0.0;
25635681Sbostic         int m,n,i;
25731856Szliu #if defined(vax)||defined(tahoe)
25824581Szliu         int k=54;
25931856Szliu #else	/* defined(vax)||defined(tahoe) */
26024581Szliu         int k=51;
26131856Szliu #endif	/* defined(vax)||defined(tahoe) */
26224581Szliu 
26324581Szliu     /* sqrt(NaN) is NaN, sqrt(+-0) = +-0 */
26424581Szliu         if(x!=x||x==zero) return(x);
26524581Szliu 
26624581Szliu     /* sqrt(negative) is invalid */
26731828Szliu         if(x<zero) {
26831856Szliu #if defined(vax)||defined(tahoe)
26931828Szliu 		return (infnan(EDOM));	/* NaN */
27031856Szliu #else	/* defined(vax)||defined(tahoe) */
27131828Szliu 		return(zero/zero);
27231856Szliu #endif	/* defined(vax)||defined(tahoe) */
27331828Szliu 	}
27424581Szliu 
27524581Szliu     /* sqrt(INF) is INF */
27624581Szliu         if(!finite(x)) return(x);
27724581Szliu 
27824581Szliu     /* scale x to [1,4) */
27924581Szliu         n=logb(x);
28024581Szliu         x=scalb(x,-n);
28124581Szliu         if((m=logb(x))!=0) x=scalb(x,-m);       /* subnormal number */
28224581Szliu         m += n;
28324581Szliu         n = m/2;
28424581Szliu         if((n+n)!=m) {x *= 2; m -=1; n=m/2;}
28524581Szliu 
28624581Szliu     /* generate sqrt(x) bit by bit (accumulating in q) */
28724581Szliu             q=1.0; s=4.0; x -= 1.0; r=1;
28824581Szliu             for(i=1;i<=k;i++) {
28924581Szliu                 t=s+1; x *= 4; r /= 2;
29024581Szliu                 if(t<=x) {
29124581Szliu                     s=t+t+2, x -= t; q += r;}
29224581Szliu                 else
29324581Szliu                     s *= 2;
29424581Szliu                 }
29524581Szliu 
29624581Szliu     /* generate the last bit and determine the final rounding */
29724581Szliu             r/=2; x *= 4;
29824581Szliu             if(x==zero) goto end; 100+r; /* trigger inexact flag */
29924581Szliu             if(s<x) {
30024581Szliu                 q+=r; x -=s; s += 2; s *= 2; x *= 4;
30124581Szliu                 t = (x-s)-5;
30224581Szliu                 b=1.0+3*r/4; if(b==1.0) goto end; /* b==1 : Round-to-zero */
30324581Szliu                 b=1.0+r/4;   if(b>1.0) t=1;	/* b>1 : Round-to-(+INF) */
30424581Szliu                 if(t>=0) q+=r; }	      /* else: Round-to-nearest */
30524581Szliu             else {
30624581Szliu                 s *= 2; x *= 4;
30724581Szliu                 t = (x-s)-1;
30824581Szliu                 b=1.0+3*r/4; if(b==1.0) goto end;
30924581Szliu                 b=1.0+r/4;   if(b>1.0) t=1;
31024581Szliu                 if(t>=0) q+=r; }
31124581Szliu 
31224581Szliu end:        return(scalb(q,n));
31324581Szliu }
31424581Szliu 
31524581Szliu #if 0
31624581Szliu /* DREM(X,Y)
31724581Szliu  * RETURN X REM Y =X-N*Y, N=[X/Y] ROUNDED (ROUNDED TO EVEN IN THE HALF WAY CASE)
31824581Szliu  * DOUBLE PRECISION (VAX D format 56 bits, IEEE DOUBLE 53 BITS)
31924581Szliu  * INTENDED FOR ASSEMBLY LANGUAGE
32024581Szliu  * CODED IN C BY K.C. NG, 3/23/85, 4/8/85.
32124581Szliu  *
32224581Szliu  * Warning: this code should not get compiled in unless ALL of
32324581Szliu  * the following machine-dependent routines are supplied.
32424581Szliu  *
32524581Szliu  * Required machine dependent functions (not on a VAX):
32624581Szliu  *     swapINX(i): save inexact flag and reset it to "i"
32724581Szliu  *     swapENI(e): save inexact enable and reset it to "e"
32824581Szliu  */
32924581Szliu 
33024581Szliu double drem(x,y)
33124581Szliu double x,y;
33224581Szliu {
33324581Szliu 
33431856Szliu #ifdef national		/* order of words in floating point number */
33535681Sbostic 	static const n0=3,n1=2,n2=1,n3=0;
33631789Szliu #else /* VAX, SUN, ZILOG, TAHOE */
33735681Sbostic 	static const n0=0,n1=1,n2=2,n3=3;
33824581Szliu #endif
33924581Szliu 
34035681Sbostic     	static const unsigned short mexp =0x7ff0, m25 =0x0190, m57 =0x0390;
34135681Sbostic 	static const double zero=0.0;
34224581Szliu 	double hy,y1,t,t1;
34324581Szliu 	short k;
34424581Szliu 	long n;
34524581Szliu 	int i,e;
34624581Szliu 	unsigned short xexp,yexp, *px  =(unsigned short *) &x  ,
34724581Szliu 	      		nx,nf,	  *py  =(unsigned short *) &y  ,
34824581Szliu 	      		sign,	  *pt  =(unsigned short *) &t  ,
34924581Szliu 	      			  *pt1 =(unsigned short *) &t1 ;
35024581Szliu 
35124581Szliu 	xexp = px[n0] & mexp ;	/* exponent of x */
35224581Szliu 	yexp = py[n0] & mexp ;	/* exponent of y */
35324581Szliu 	sign = px[n0] &0x8000;	/* sign of x     */
35424581Szliu 
35524581Szliu /* return NaN if x is NaN, or y is NaN, or x is INF, or y is zero */
35624581Szliu 	if(x!=x) return(x); if(y!=y) return(y);	     /* x or y is NaN */
35724581Szliu 	if( xexp == mexp )   return(zero/zero);      /* x is INF */
35824581Szliu 	if(y==zero) return(y/y);
35924581Szliu 
36024581Szliu /* save the inexact flag and inexact enable in i and e respectively
36124581Szliu  * and reset them to zero
36224581Szliu  */
36324581Szliu 	i=swapINX(0);	e=swapENI(0);
36424581Szliu 
36524581Szliu /* subnormal number */
36624581Szliu 	nx=0;
36724581Szliu 	if(yexp==0) {t=1.0,pt[n0]+=m57; y*=t; nx=m57;}
36824581Szliu 
36924581Szliu /* if y is tiny (biased exponent <= 57), scale up y to y*2**57 */
37024581Szliu 	if( yexp <= m57 ) {py[n0]+=m57; nx+=m57; yexp+=m57;}
37124581Szliu 
37224581Szliu 	nf=nx;
37324581Szliu 	py[n0] &= 0x7fff;
37424581Szliu 	px[n0] &= 0x7fff;
37524581Szliu 
37624581Szliu /* mask off the least significant 27 bits of y */
37724581Szliu 	t=y; pt[n3]=0; pt[n2]&=0xf800; y1=t;
37824581Szliu 
37924581Szliu /* LOOP: argument reduction on x whenever x > y */
38024581Szliu loop:
38124581Szliu 	while ( x > y )
38224581Szliu 	{
38324581Szliu 	    t=y;
38424581Szliu 	    t1=y1;
38524581Szliu 	    xexp=px[n0]&mexp;	  /* exponent of x */
38624581Szliu 	    k=xexp-yexp-m25;
38724581Szliu 	    if(k>0) 	/* if x/y >= 2**26, scale up y so that x/y < 2**26 */
38824581Szliu 		{pt[n0]+=k;pt1[n0]+=k;}
38924581Szliu 	    n=x/t; x=(x-n*t1)-n*(t-t1);
39024581Szliu 	}
39124581Szliu     /* end while (x > y) */
39224581Szliu 
39324581Szliu 	if(nx!=0) {t=1.0; pt[n0]+=nx; x*=t; nx=0; goto loop;}
39424581Szliu 
39524581Szliu /* final adjustment */
39624581Szliu 
39724581Szliu 	hy=y/2.0;
39824581Szliu 	if(x>hy||((x==hy)&&n%2==1)) x-=y;
39924581Szliu 	px[n0] ^= sign;
40024581Szliu 	if(nf!=0) { t=1.0; pt[n0]-=nf; x*=t;}
40124581Szliu 
40224581Szliu /* restore inexact flag and inexact enable */
40324581Szliu 	swapINX(i); swapENI(e);
40424581Szliu 
40524581Szliu 	return(x);
40624581Szliu }
40724581Szliu #endif
40824581Szliu 
40924581Szliu #if 0
41024581Szliu /* SQRT
41124581Szliu  * RETURN CORRECTLY ROUNDED (ACCORDING TO THE ROUNDING MODE) SQRT
41224581Szliu  * FOR IEEE DOUBLE PRECISION ONLY, INTENDED FOR ASSEMBLY LANGUAGE
41324581Szliu  * CODED IN C BY K.C. NG, 3/22/85.
41424581Szliu  *
41524581Szliu  * Warning: this code should not get compiled in unless ALL of
41624581Szliu  * the following machine-dependent routines are supplied.
41724581Szliu  *
41824581Szliu  * Required machine dependent functions:
41924581Szliu  *     swapINX(i)  ...return the status of INEXACT flag and reset it to "i"
42024581Szliu  *     swapRM(r)   ...return the current Rounding Mode and reset it to "r"
42124581Szliu  *     swapENI(e)  ...return the status of inexact enable and reset it to "e"
42224581Szliu  *     addc(t)     ...perform t=t+1 regarding t as a 64 bit unsigned integer
42324581Szliu  *     subc(t)     ...perform t=t-1 regarding t as a 64 bit unsigned integer
42424581Szliu  */
42524581Szliu 
42635681Sbostic static const unsigned long table[] = {
42724581Szliu 0, 1204, 3062, 5746, 9193, 13348, 18162, 23592, 29598, 36145, 43202, 50740,
42824581Szliu 58733, 67158, 75992, 85215, 83599, 71378, 60428, 50647, 41945, 34246, 27478,
42924581Szliu 21581, 16499, 12183, 8588, 5674, 3403, 1742, 661, 130, };
43024581Szliu 
43124581Szliu double newsqrt(x)
43224581Szliu double x;
43324581Szliu {
43435681Sbostic         double y,z,t,addc(),subc()
43535681Sbostic 	double const b54=134217728.*134217728.; /* b54=2**54 */
43635681Sbostic         long mx,scalx;
43735681Sbostic 	long const mexp=0x7ff00000;
43824581Szliu         int i,j,r,e,swapINX(),swapRM(),swapENI();
43924581Szliu         unsigned long *py=(unsigned long *) &y   ,
44024581Szliu                       *pt=(unsigned long *) &t   ,
44124581Szliu                       *px=(unsigned long *) &x   ;
44231856Szliu #ifdef national         /* ordering of word in a floating point number */
44335681Sbostic         const int n0=1, n1=0;
44424581Szliu #else
44535681Sbostic         const int n0=0, n1=1;
44624581Szliu #endif
44724581Szliu /* Rounding Mode:  RN ...round-to-nearest
44824581Szliu  *                 RZ ...round-towards 0
44924581Szliu  *                 RP ...round-towards +INF
45024581Szliu  *		   RM ...round-towards -INF
45124581Szliu  */
45235681Sbostic         const int RN=0,RZ=1,RP=2,RM=3;
45335681Sbostic 				/* machine dependent: work on a Zilog Z8070
45424581Szliu                                  * and a National 32081 & 16081
45524581Szliu                                  */
45624581Szliu 
45724581Szliu /* exceptions */
45824581Szliu 	if(x!=x||x==0.0) return(x);  /* sqrt(NaN) is NaN, sqrt(+-0) = +-0 */
45924581Szliu 	if(x<0) return((x-x)/(x-x)); /* sqrt(negative) is invalid */
46024581Szliu         if((mx=px[n0]&mexp)==mexp) return(x);  /* sqrt(+INF) is +INF */
46124581Szliu 
46224581Szliu /* save, reset, initialize */
46324581Szliu         e=swapENI(0);   /* ...save and reset the inexact enable */
46424581Szliu         i=swapINX(0);   /* ...save INEXACT flag */
46524581Szliu         r=swapRM(RN);   /* ...save and reset the Rounding Mode to RN */
46624581Szliu         scalx=0;
46724581Szliu 
46824581Szliu /* subnormal number, scale up x to x*2**54 */
46924581Szliu         if(mx==0) {x *= b54 ; scalx-=0x01b00000;}
47024581Szliu 
47124581Szliu /* scale x to avoid intermediate over/underflow:
47224581Szliu  * if (x > 2**512) x=x/2**512; if (x < 2**-512) x=x*2**512 */
47324581Szliu         if(mx>0x5ff00000) {px[n0] -= 0x20000000; scalx+= 0x10000000;}
47424581Szliu         if(mx<0x1ff00000) {px[n0] += 0x20000000; scalx-= 0x10000000;}
47524581Szliu 
47624581Szliu /* magic initial approximation to almost 8 sig. bits */
47724581Szliu         py[n0]=(px[n0]>>1)+0x1ff80000;
47824581Szliu         py[n0]=py[n0]-table[(py[n0]>>15)&31];
47924581Szliu 
48024581Szliu /* Heron's rule once with correction to improve y to almost 18 sig. bits */
48124581Szliu         t=x/y; y=y+t; py[n0]=py[n0]-0x00100006; py[n1]=0;
48224581Szliu 
48324581Szliu /* triple to almost 56 sig. bits; now y approx. sqrt(x) to within 1 ulp */
48424581Szliu         t=y*y; z=t;  pt[n0]+=0x00100000; t+=z; z=(x-z)*y;
48524581Szliu         t=z/(t+x) ;  pt[n0]+=0x00100000; y+=t;
48624581Szliu 
48724581Szliu /* twiddle last bit to force y correctly rounded */
48824581Szliu         swapRM(RZ);     /* ...set Rounding Mode to round-toward-zero */
48924581Szliu         swapINX(0);     /* ...clear INEXACT flag */
49024581Szliu         swapENI(e);     /* ...restore inexact enable status */
49124581Szliu         t=x/y;          /* ...chopped quotient, possibly inexact */
49224581Szliu         j=swapINX(i);   /* ...read and restore inexact flag */
49324581Szliu         if(j==0) { if(t==y) goto end; else t=subc(t); }  /* ...t=t-ulp */
49424581Szliu         b54+0.1;        /* ..trigger inexact flag, sqrt(x) is inexact */
49524581Szliu         if(r==RN) t=addc(t);            /* ...t=t+ulp */
49624581Szliu         else if(r==RP) { t=addc(t);y=addc(y);}/* ...t=t+ulp;y=y+ulp; */
49724581Szliu         y=y+t;                          /* ...chopped sum */
49824581Szliu         py[n0]=py[n0]-0x00100000;       /* ...correctly rounded sqrt(x) */
49924581Szliu end:    py[n0]=py[n0]+scalx;            /* ...scale back y */
50024581Szliu         swapRM(r);                      /* ...restore Rounding Mode */
50124581Szliu         return(y);
50224581Szliu }
50324581Szliu #endif
504