1570af302Sopenharmony_ci#include <math.h> 2570af302Sopenharmony_ci#include <stdint.h> 3570af302Sopenharmony_ci#include <float.h> 4570af302Sopenharmony_ci 5570af302Sopenharmony_ci#if FLT_EVAL_METHOD > 1U && LDBL_MANT_DIG == 64 6570af302Sopenharmony_ci#define SPLIT (0x1p32 + 1) 7570af302Sopenharmony_ci#else 8570af302Sopenharmony_ci#define SPLIT (0x1p27 + 1) 9570af302Sopenharmony_ci#endif 10570af302Sopenharmony_ci 11570af302Sopenharmony_cistatic void sq(double_t *hi, double_t *lo, double x) 12570af302Sopenharmony_ci{ 13570af302Sopenharmony_ci double_t xh, xl, xc; 14570af302Sopenharmony_ci 15570af302Sopenharmony_ci xc = (double_t)x*SPLIT; 16570af302Sopenharmony_ci xh = x - xc + xc; 17570af302Sopenharmony_ci xl = x - xh; 18570af302Sopenharmony_ci *hi = (double_t)x*x; 19570af302Sopenharmony_ci *lo = xh*xh - *hi + 2*xh*xl + xl*xl; 20570af302Sopenharmony_ci} 21570af302Sopenharmony_ci 22570af302Sopenharmony_cidouble hypot(double x, double y) 23570af302Sopenharmony_ci{ 24570af302Sopenharmony_ci union {double f; uint64_t i;} ux = {x}, uy = {y}, ut; 25570af302Sopenharmony_ci int ex, ey; 26570af302Sopenharmony_ci double_t hx, lx, hy, ly, z; 27570af302Sopenharmony_ci 28570af302Sopenharmony_ci /* arrange |x| >= |y| */ 29570af302Sopenharmony_ci ux.i &= -1ULL>>1; 30570af302Sopenharmony_ci uy.i &= -1ULL>>1; 31570af302Sopenharmony_ci if (ux.i < uy.i) { 32570af302Sopenharmony_ci ut = ux; 33570af302Sopenharmony_ci ux = uy; 34570af302Sopenharmony_ci uy = ut; 35570af302Sopenharmony_ci } 36570af302Sopenharmony_ci 37570af302Sopenharmony_ci /* special cases */ 38570af302Sopenharmony_ci ex = ux.i>>52; 39570af302Sopenharmony_ci ey = uy.i>>52; 40570af302Sopenharmony_ci x = ux.f; 41570af302Sopenharmony_ci y = uy.f; 42570af302Sopenharmony_ci /* note: hypot(inf,nan) == inf */ 43570af302Sopenharmony_ci if (ey == 0x7ff) 44570af302Sopenharmony_ci return y; 45570af302Sopenharmony_ci if (ex == 0x7ff || uy.i == 0) 46570af302Sopenharmony_ci return x; 47570af302Sopenharmony_ci /* note: hypot(x,y) ~= x + y*y/x/2 with inexact for small y/x */ 48570af302Sopenharmony_ci /* 64 difference is enough for ld80 double_t */ 49570af302Sopenharmony_ci if (ex - ey > 64) 50570af302Sopenharmony_ci return x + y; 51570af302Sopenharmony_ci 52570af302Sopenharmony_ci /* precise sqrt argument in nearest rounding mode without overflow */ 53570af302Sopenharmony_ci /* xh*xh must not overflow and xl*xl must not underflow in sq */ 54570af302Sopenharmony_ci z = 1; 55570af302Sopenharmony_ci if (ex > 0x3ff+510) { 56570af302Sopenharmony_ci z = 0x1p700; 57570af302Sopenharmony_ci x *= 0x1p-700; 58570af302Sopenharmony_ci y *= 0x1p-700; 59570af302Sopenharmony_ci } else if (ey < 0x3ff-450) { 60570af302Sopenharmony_ci z = 0x1p-700; 61570af302Sopenharmony_ci x *= 0x1p700; 62570af302Sopenharmony_ci y *= 0x1p700; 63570af302Sopenharmony_ci } 64570af302Sopenharmony_ci sq(&hx, &lx, x); 65570af302Sopenharmony_ci sq(&hy, &ly, y); 66570af302Sopenharmony_ci return z*sqrt(ly+lx+hy+hx); 67570af302Sopenharmony_ci} 68