hypotl.c 3.7 KB
Newer Older
R
Rich Felker 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
/* origin: FreeBSD /usr/src/lib/msun/src/e_hypotl.c */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunSoft, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice
 * is preserved.
 * ====================================================
 */
/* long double version of hypot().  See comments in hypot.c. */

#include "libm.h"

#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
long double hypotl(long double x, long double y)
{
	return hypot(x, y);
}
#elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384

#define GET_LDBL_EXPSIGN(i, v) do {     \
	union IEEEl2bits uv;            \
					\
	uv.e = v;                       \
	i = uv.xbits.expsign;           \
} while (0)

#define GET_LDBL_MAN(h, l, v) do {      \
	union IEEEl2bits uv;            \
					\
	uv.e = v;                       \
	h = uv.bits.manh;               \
	l = uv.bits.manl;               \
} while (0)

#define SET_LDBL_EXPSIGN(v, i) do {     \
	union IEEEl2bits uv;            \
					\
	uv.e = v;                       \
	uv.xbits.expsign = i;           \
	v = uv.e;                       \
} while (0)

#undef GET_HIGH_WORD
#define GET_HIGH_WORD(i, v)     GET_LDBL_EXPSIGN(i, v)
#undef SET_HIGH_WORD
#define SET_HIGH_WORD(v, i)     SET_LDBL_EXPSIGN(v, i)

#define DESW(exp)       (exp)           /* delta expsign word */
#define ESW(exp)        (MAX_EXP - 1 + (exp))   /* expsign word */
#define MANT_DIG        LDBL_MANT_DIG
#define MAX_EXP         LDBL_MAX_EXP

#if LDBL_MANL_SIZE > 32
typedef uint64_t man_t;
#else
typedef uint32_t man_t;
#endif

long double hypotl(long double x, long double y)
{
	long double a=x,b=y,t1,t2,y1,y2,w;
	int32_t j,k,ha,hb;

	GET_HIGH_WORD(ha, x);
	ha &= 0x7fff;
	GET_HIGH_WORD(hb, y);
	hb &= 0x7fff;
	if (hb > ha) {
		a = y;
		b = x;
		j=ha; ha=hb; hb=j;
	} else {
		a = x;
		b = y;
	}
	a = fabsl(a);
	b = fabsl(b);
	if (ha - hb > DESW(MANT_DIG+7))  /* x/y > 2**(MANT_DIG+7) */
		return a+b;
	k = 0;
	if (ha > ESW(MAX_EXP/2-12)) {    /* a>2**(MAX_EXP/2-12) */
		if (ha >= ESW(MAX_EXP)) {  /* Inf or NaN */
			man_t manh, manl;
			/* Use original arg order iff result is NaN; quieten sNaNs. */
			w = fabsl(x+0.0)-fabsl(y+0.0);
			GET_LDBL_MAN(manh,manl,a);
			if (manh == LDBL_NBIT && manl == 0) w = a;
			GET_LDBL_MAN(manh,manl,b);
			if (hb >= ESW(MAX_EXP) && manh == LDBL_NBIT && manl == 0) w = b;
			return w;
		}
		/* scale a and b by 2**-(MAX_EXP/2+88) */
		ha -= DESW(MAX_EXP/2+88); hb -= DESW(MAX_EXP/2+88);
		k += MAX_EXP/2+88;
		SET_HIGH_WORD(a, ha);
		SET_HIGH_WORD(b, hb);
	}
	if (hb < ESW(-(MAX_EXP/2-12))) {  /* b < 2**-(MAX_EXP/2-12) */
		if (hb <= 0) {  /* subnormal b or 0 */
			man_t manh, manl;
			GET_LDBL_MAN(manh,manl,b);
			if ((manh|manl) == 0)
				return a;
			t1 = 0;
			SET_HIGH_WORD(t1, ESW(MAX_EXP-2));  /* t1 = 2^(MAX_EXP-2) */
			b *= t1;
			a *= t1;
			k -= MAX_EXP-2;
		} else {            /* scale a and b by 2^(MAX_EXP/2+88) */
			ha += DESW(MAX_EXP/2+88);
			hb += DESW(MAX_EXP/2+88);
			k -= MAX_EXP/2+88;
			SET_HIGH_WORD(a, ha);
			SET_HIGH_WORD(b, hb);
		}
	}
	/* medium size a and b */
	w = a - b;
	if (w > b) {
		t1 = a;
		union IEEEl2bits uv;
		uv.e = t1; uv.bits.manl = 0; t1 = uv.e;
		t2 = a-t1;
		w  = sqrtl(t1*t1-(b*(-b)-t2*(a+t1)));
	} else {
		a  = a+a;
		y1 = b;
		union IEEEl2bits uv;
		uv.e = y1; uv.bits.manl = 0; y1 = uv.e;
		y2 = b - y1;
		t1 = a;
		uv.e = t1; uv.bits.manl = 0; t1 = uv.e;
		t2 = a - t1;
		w  = sqrtl(t1*y1-(w*(-w)-(t1*y2+t2*b)));
	}
	if(k!=0) {
		uint32_t high;
		t1 = 1.0;
		GET_HIGH_WORD(high, t1);
		SET_HIGH_WORD(t1, high+DESW(k));
		return t1*w;
	}
	return w;
}
#endif