#include "quad.h"
static const double C[] = {
0.0,
0.5,
1.0,
2.0,
68719476736.0,
1048576.0,
16.0,
1.52587890625000000000e-05,
5.96046447753906250000e-08,
3.72529029846191406250e-09,
8.67361737988403547206e-19,
2.16840434497100886801e-19,
1.32348898008484427979e-23,
9.62964972193617926528e-35,
4.70197740328915003187e-38
};
#define zero C[0]
#define half C[1]
#define one C[2]
#define two C[3]
#define two36 C[4]
#define two20 C[5]
#define two4 C[6]
#define twom16 C[7]
#define twom24 C[8]
#define twom28 C[9]
#define twom60 C[10]
#define twom62 C[11]
#define twom76 C[12]
#define twom113 C[13]
#define twom124 C[14]
static const unsigned fsr_rn = 0xc0000000u;
#ifdef __sparcv9
void
_Qp_mul(union longdouble *pz, const union longdouble *x,
const union longdouble *y)
#else
union longdouble
_Q_mul(const union longdouble *x, const union longdouble *y)
#endif
{
union longdouble z;
union xdouble u;
double xx[5], yy[5], c, d, zz[9];
unsigned int xm, ym, fsr, lx, ly, wx[3], wy[3];
unsigned int msw, frac2, frac3, frac4, rm;
int ibit, ex, ey, ez, sign;
xm = x->l.msw & 0x7fffffff;
ym = y->l.msw & 0x7fffffff;
sign = (x->l.msw ^ y->l.msw) & ~0x7fffffff;
__quad_getfsrp(&fsr);
if (xm >= 0x7fff0000 || ym >= 0x7fff0000) {
if (QUAD_ISNAN(*y)) {
if (!(y->l.msw & 0x8000)) {
if (fsr & FSR_NVM) {
__quad_fmulq(x, y, &Z);
} else {
Z = *y;
Z.l.msw |= 0x8000;
fsr = (fsr & ~FSR_CEXC) | FSR_NVA |
FSR_NVC;
__quad_setfsrp(&fsr);
}
QUAD_RETURN(Z);
} else if (QUAD_ISNAN(*x) && !(x->l.msw & 0x8000)) {
if (fsr & FSR_NVM) {
__quad_fmulq(x, y, &Z);
} else {
Z = *x;
Z.l.msw |= 0x8000;
fsr = (fsr & ~FSR_CEXC) | FSR_NVA |
FSR_NVC;
__quad_setfsrp(&fsr);
}
QUAD_RETURN(Z);
}
Z = *y;
QUAD_RETURN(Z);
}
if (QUAD_ISNAN(*x)) {
if (!(x->l.msw & 0x8000)) {
if (fsr & FSR_NVM) {
__quad_fmulq(x, y, &Z);
} else {
Z = *x;
Z.l.msw |= 0x8000;
fsr = (fsr & ~FSR_CEXC) | FSR_NVA |
FSR_NVC;
__quad_setfsrp(&fsr);
}
QUAD_RETURN(Z);
}
Z = *x;
QUAD_RETURN(Z);
}
if (xm == 0x7fff0000) {
if (QUAD_ISZERO(*y)) {
if (fsr & FSR_NVM) {
__quad_fmulq(x, y, &Z);
} else {
Z.l.msw = 0x7fffffff;
Z.l.frac2 = Z.l.frac3 =
Z.l.frac4 = 0xffffffff;
fsr = (fsr & ~FSR_CEXC) | FSR_NVA |
FSR_NVC;
__quad_setfsrp(&fsr);
}
QUAD_RETURN(Z);
}
Z.l.msw = sign | 0x7fff0000;
Z.l.frac2 = Z.l.frac3 = Z.l.frac4 = 0;
QUAD_RETURN(Z);
}
if (QUAD_ISZERO(*x)) {
if (fsr & FSR_NVM) {
__quad_fmulq(x, y, &Z);
} else {
Z.l.msw = 0x7fffffff;
Z.l.frac2 = Z.l.frac3 = Z.l.frac4 = 0xffffffff;
fsr = (fsr & ~FSR_CEXC) | FSR_NVA | FSR_NVC;
__quad_setfsrp(&fsr);
}
QUAD_RETURN(Z);
}
Z.l.msw = sign | 0x7fff0000;
Z.l.frac2 = Z.l.frac3 = Z.l.frac4 = 0;
QUAD_RETURN(Z);
}
if (xm == 0 || ym == 0) {
if (QUAD_ISZERO(*x) || QUAD_ISZERO(*y)) {
Z.l.msw = sign;
Z.l.frac2 = Z.l.frac3 = Z.l.frac4 = 0;
QUAD_RETURN(Z);
}
}
__quad_setfsrp(&fsr_rn);
ex = (int)(xm >> 16);
lx = xm & 0xffff;
if (ex) {
lx |= 0x10000;
wx[0] = x->l.frac2;
wx[1] = x->l.frac3;
wx[2] = x->l.frac4;
} else {
if (lx | (x->l.frac2 & 0xfffe0000)) {
wx[0] = x->l.frac2;
wx[1] = x->l.frac3;
wx[2] = x->l.frac4;
ex = 1;
} else if (x->l.frac2 | (x->l.frac3 & 0xfffe0000)) {
lx = x->l.frac2;
wx[0] = x->l.frac3;
wx[1] = x->l.frac4;
wx[2] = 0;
ex = -31;
} else if (x->l.frac3 | (x->l.frac4 & 0xfffe0000)) {
lx = x->l.frac3;
wx[0] = x->l.frac4;
wx[1] = wx[2] = 0;
ex = -63;
} else {
lx = x->l.frac4;
wx[0] = wx[1] = wx[2] = 0;
ex = -95;
}
while ((lx & 0x10000) == 0) {
lx = (lx << 1) | (wx[0] >> 31);
wx[0] = (wx[0] << 1) | (wx[1] >> 31);
wx[1] = (wx[1] << 1) | (wx[2] >> 31);
wx[2] <<= 1;
ex--;
}
}
ez = ex - 0x3fff;
ey = (int)(ym >> 16);
ly = ym & 0xffff;
if (ey) {
ly |= 0x10000;
wy[0] = y->l.frac2;
wy[1] = y->l.frac3;
wy[2] = y->l.frac4;
} else {
if (ly | (y->l.frac2 & 0xfffe0000)) {
wy[0] = y->l.frac2;
wy[1] = y->l.frac3;
wy[2] = y->l.frac4;
ey = 1;
} else if (y->l.frac2 | (y->l.frac3 & 0xfffe0000)) {
ly = y->l.frac2;
wy[0] = y->l.frac3;
wy[1] = y->l.frac4;
wy[2] = 0;
ey = -31;
} else if (y->l.frac3 | (y->l.frac4 & 0xfffe0000)) {
ly = y->l.frac3;
wy[0] = y->l.frac4;
wy[1] = wy[2] = 0;
ey = -63;
} else {
ly = y->l.frac4;
wy[0] = wy[1] = wy[2] = 0;
ey = -95;
}
while ((ly & 0x10000) == 0) {
ly = (ly << 1) | (wy[0] >> 31);
wy[0] = (wy[0] << 1) | (wy[1] >> 31);
wy[1] = (wy[1] << 1) | (wy[2] >> 31);
wy[2] <<= 1;
ey--;
}
}
ez += ey;
c = twom16;
xx[0] = (double)((int)lx) * c;
yy[0] = (double)((int)ly) * c;
c *= twom24;
xx[1] = (double)((int)(wx[0] >> 8)) * c;
yy[1] = (double)((int)(wy[0] >> 8)) * c;
c *= twom24;
xx[2] = (double)((int)(((wx[0] << 16) | (wx[1] >> 16)) &
0xffffff)) * c;
yy[2] = (double)((int)(((wy[0] << 16) | (wy[1] >> 16)) &
0xffffff)) * c;
c *= twom24;
xx[3] = (double)((int)(((wx[1] << 8) | (wx[2] >> 24)) &
0xffffff)) * c;
yy[3] = (double)((int)(((wy[1] << 8) | (wy[2] >> 24)) &
0xffffff)) * c;
c *= twom24;
xx[4] = (double)((int)(wx[2] & 0xffffff)) * c;
yy[4] = (double)((int)(wy[2] & 0xffffff)) * c;
zz[0] = xx[0] * yy[0];
zz[1] = xx[0] * yy[1] + xx[1] * yy[0];
zz[2] = xx[0] * yy[2] + xx[1] * yy[1] + xx[2] * yy[0];
zz[3] = xx[0] * yy[3] + xx[1] * yy[2] + xx[2] * yy[1] +
xx[3] * yy[0];
zz[4] = xx[0] * yy[4] + xx[1] * yy[3] + xx[2] * yy[2] +
xx[3] * yy[1] + xx[4] * yy[0];
zz[5] = xx[1] * yy[4] + xx[2] * yy[3] + xx[3] * yy[2] +
xx[4] * yy[1];
zz[6] = xx[2] * yy[4] + xx[3] * yy[3] + xx[4] * yy[2];
zz[7] = xx[3] * yy[4] + xx[4] * yy[3];
zz[8] = xx[4] * yy[4];
c = (zz[1] + two20) - two20;
zz[0] += c;
zz[1] = zz[2] + (zz[1] - c);
c = (zz[3] + twom28) - twom28;
zz[1] += c;
zz[2] = zz[4] + (zz[3] - c);
d = zz[6] + (zz[7] + zz[8]);
zz[2] += zz[5] + d;
if (zz[2] == (twom62 + zz[2]) - twom62)
{
c = (zz[5] + twom76) - twom76;
if ((zz[5] - c) + d != zero)
zz[2] += twom124;
}
c = zz[1] + zz[2];
zz[2] = zz[2] + (zz[1] - c);
zz[1] = c;
c = zz[0] + zz[1];
zz[1] = zz[1] + (zz[0] - c);
zz[0] = c;
if (c >= two) {
zz[0] *= half;
zz[1] *= half;
zz[2] *= half;
ez++;
}
if (ez > 0) {
ibit = 1;
zz[0] -= one;
} else {
ibit = 0;
if (ez > -128)
u.l.hi = (unsigned)(0x3fe + ez) << 20;
else
u.l.hi = 0x37e00000;
u.l.lo = 0;
zz[0] *= u.d;
zz[1] *= u.d;
zz[2] *= u.d;
ez = 0;
}
u.d = d = two36 + zz[0];
msw = u.l.lo;
zz[0] -= (d - two36);
u.d = d = two4 + zz[0];
frac2 = u.l.lo;
zz[0] -= (d - two4);
u.d = d = twom28 + (zz[0] + zz[1]);
frac3 = u.l.lo;
zz[0] -= (d - twom28);
c = zz[0] + zz[1];
zz[1] = ((zz[0] - c) + zz[1]) + zz[2];
zz[0] = c;
u.d = d = twom60 + (zz[0] + zz[1]);
frac4 = u.l.lo;
zz[0] -= (d - twom60);
c = zz[0] + zz[1];
rm = fsr >> 30;
if (sign)
rm ^= (rm >> 1);
fsr &= ~FSR_CEXC;
if (c != zero) {
fsr |= FSR_NXC;
if (rm == FSR_RP || (rm == FSR_RN && (c > twom113 ||
(c == twom113 && ((frac4 & 1) || (c - zz[0] != zz[1])))))) {
if (++frac4 == 0)
if (++frac3 == 0)
if (++frac2 == 0)
if (++msw == 0x10000) {
msw = 0;
ez++;
}
}
}
if (ez >= 0x7fff) {
if (rm == FSR_RN || rm == FSR_RP) {
z.l.msw = sign | 0x7fff0000;
z.l.frac2 = z.l.frac3 = z.l.frac4 = 0;
} else {
z.l.msw = sign | 0x7ffeffff;
z.l.frac2 = z.l.frac3 = z.l.frac4 = 0xffffffff;
}
fsr |= FSR_OFC | FSR_NXC;
} else {
z.l.msw = sign | (ez << 16) | msw;
z.l.frac2 = frac2;
z.l.frac3 = frac3;
z.l.frac4 = frac4;
if (!ibit) {
if (c != zero)
fsr |= FSR_UFC | FSR_NXC;
else if (fsr & FSR_UFM)
fsr |= FSR_UFC;
}
}
if ((fsr & FSR_CEXC) & (fsr >> 23)) {
__quad_setfsrp(&fsr);
__quad_fmulq(x, y, &Z);
} else {
Z = z;
fsr |= (fsr & 0x1f) << 5;
__quad_setfsrp(&fsr);
}
QUAD_RETURN(Z);
}