xref: /dragonfly/contrib/openbsd_libm/src/ld80/e_hypotl.c (revision 4382f29d99a100bd77a81697c2f699c11f6a472a)
1 /* @(#)e_hypot.c 5.1 93/09/24 */
2 /*
3  * ====================================================
4  * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
5  *
6  * Developed at SunPro, a Sun Microsystems, Inc. business.
7  * Permission to use, copy, modify, and distribute this
8  * software is freely granted, provided that this notice
9  * is preserved.
10  * ====================================================
11  */
12 
13 /* hypotl(x,y)
14  *
15  * Method :
16  *        If (assume round-to-nearest) z=x*x+y*y
17  *        has error less than sqrt(2)/2 ulp, than
18  *        sqrt(z) has error less than 1 ulp (exercise).
19  *
20  *        So, compute sqrt(x*x+y*y) with some care as
21  *        follows to get the error below 1 ulp:
22  *
23  *        Assume x>y>0;
24  *        (if possible, set rounding to round-to-nearest)
25  *        1. if x > 2y  use
26  *                  x1*x1+(y*y+(x2*(x+x1))) for x*x+y*y
27  *        where x1 = x with lower 32 bits cleared, x2 = x-x1; else
28  *        2. if x <= 2y use
29  *                  t1*yy1+((x-y)*(x-y)+(t1*y2+t2*y))
30  *        where t1 = 2x with lower 32 bits cleared, t2 = 2x-t1,
31  *        yy1= y with lower 32 bits chopped, y2 = y-yy1.
32  *
33  *        NOTE: scaling may be necessary if some argument is too
34  *              large or too tiny
35  *
36  * Special cases:
37  *        hypot(x,y) is INF if x or y is +INF or -INF; else
38  *        hypot(x,y) is NAN if x or y is NAN.
39  *
40  * Accuracy:
41  *        hypot(x,y) returns sqrt(x^2+y^2) with error less
42  *        than 1 ulps (units in the last place)
43  */
44 
45 #include <math.h>
46 
47 #include "math_private.h"
48 
49 long double
hypotl(long double x,long double y)50 hypotl(long double x, long double y)
51 {
52           long double a,b,t1,t2,yy1,y2,w;
53           u_int32_t j,k,ea,eb;
54 
55           GET_LDOUBLE_EXP(ea,x);
56           ea &= 0x7fff;
57           GET_LDOUBLE_EXP(eb,y);
58           eb &= 0x7fff;
59           if(eb > ea) {a=y;b=x;j=ea; ea=eb;eb=j;} else {a=x;b=y;}
60           SET_LDOUBLE_EXP(a,ea);        /* a <- |a| */
61           SET_LDOUBLE_EXP(b,eb);        /* b <- |b| */
62           if((ea-eb)>0x46) {return a+b;} /* x/y > 2**70 */
63           k=0;
64           if(ea > 0x5f3f) {   /* a>2**8000 */
65              if(ea == 0x7fff) {         /* Inf or NaN */
66                  u_int32_t es,high,low;
67                  w = a+b;                         /* for sNaN */
68                  GET_LDOUBLE_WORDS(es,high,low,a);
69                  if(((high&0x7fffffff)|low)==0) w = a;
70                  GET_LDOUBLE_WORDS(es,high,low,b);
71                  if(((eb^0x7fff)|(high&0x7fffffff)|low)==0) w = b;
72                  return w;
73              }
74              /* scale a and b by 2**-9600 */
75              ea -= 0x2580; eb -= 0x2580;          k += 9600;
76              SET_LDOUBLE_EXP(a,ea);
77              SET_LDOUBLE_EXP(b,eb);
78           }
79           if(eb < 0x20bf) {   /* b < 2**-8000 */
80               if(eb == 0) {   /* subnormal b or 0 */
81                     u_int32_t es,high,low;
82                     GET_LDOUBLE_WORDS(es,high,low,b);
83                     if((high|low)==0) return a;
84                     SET_LDOUBLE_WORDS(t1, 0x7ffd, 0, 0);    /* t1=2^16382 */
85                     b *= t1;
86                     a *= t1;
87                     k -= 16382;
88               } else {                  /* scale a and b by 2^9600 */
89                     ea += 0x2580;       /* a *= 2^9600 */
90                     eb += 0x2580;       /* b *= 2^9600 */
91                     k -= 9600;
92                     SET_LDOUBLE_EXP(a,ea);
93                     SET_LDOUBLE_EXP(b,eb);
94               }
95           }
96     /* medium size a and b */
97           w = a-b;
98           if (w>b) {
99               u_int32_t high;
100               GET_LDOUBLE_MSW(high,a);
101               SET_LDOUBLE_WORDS(t1,ea,high,0);
102               t2 = a-t1;
103               w  = sqrtl(t1*t1-(b*(-b)-t2*(a+t1)));
104           } else {
105               u_int32_t high;
106               GET_LDOUBLE_MSW(high,b);
107               a  = a+a;
108               SET_LDOUBLE_WORDS(yy1,eb,high,0);
109               y2 = b - yy1;
110               GET_LDOUBLE_MSW(high,a);
111               SET_LDOUBLE_WORDS(t1,ea+1,high,0);
112               t2 = a - t1;
113               w  = sqrtl(t1*yy1-(w*(-w)-(t1*y2+t2*b)));
114           }
115           if(k!=0) {
116               u_int32_t es;
117               t1 = 1.0;
118               GET_LDOUBLE_EXP(es,t1);
119               SET_LDOUBLE_EXP(t1,es+k);
120               return t1*w;
121           } else return w;
122 }
123