addsub.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 1988 by Sun Microsystems, Inc.
*/
#ident "%Z%%M% %I% %E% SMI" /* SunOS-4.1 1.8 88/12/06 */
#include <sys/fpu/fpu_simulator.h>
#include <sys/fpu/globals.h>
static void
true_add(px, py, pz)
unpacked *px, *py, *pz;
{
unsigned c;
unpacked *pt;
if ((int) px->fpclass <= (int) py->fpclass) { /* Reverse. */
pt = py;
py = px;
px = pt;
}
/* Now class(x) >= class(y). */
switch (px->fpclass) {
case fp_quiet: /* NaN + x -> NaN */
case fp_signaling: /* NaN + x -> NaN */
case fp_infinity: /* Inf + x -> Inf */
case fp_zero: /* 0 + 0 -> 0 */
*pz = *px;
return;
default:
if (py->fpclass == fp_zero) {
*pz = *px;
return;
}
}
/* Now z is normal or subnormal. */
/* Now y is normal or subnormal. */
if (px->exponent < py->exponent) { /* Reverse. */
pt = py;
py = px;
px = pt;
}
/* Now class(x) >= class(y). */
pz->fpclass = px->fpclass;
pz->sign = px->sign;
pz->exponent = px->exponent;
pz->rounded = pz->sticky = 0;
if (px->exponent != py->exponent) { /* pre-alignment required */
fpu_rightshift(py, pz->exponent - py->exponent);
pz->rounded = py->rounded;
pz->sticky = py->sticky;
}
c = 0;
c = fpu_add3wc(&(pz->significand[3]), px->significand[3],
py->significand[3], c);
c = fpu_add3wc(&(pz->significand[2]), px->significand[2],
py->significand[2], c);
c = fpu_add3wc(&(pz->significand[1]), px->significand[1],
py->significand[1], c);
c = fpu_add3wc(&(pz->significand[0]), px->significand[0],
py->significand[0], c);
/* Handle carry out of msb. */
if (pz->significand[0] >= 0x20000) {
fpu_rightshift(pz, 1); /* Carried out bit. */
pz->exponent++; /* Renormalize. */
}
}
static void
true_sub(pfpsd, px, py, pz)
fp_simd_type *pfpsd; /* Pointer to simulator data */
unpacked *px, *py, *pz;
{
unsigned *z, g, s, r, c;
unpacked *pt;
if ((int) px->fpclass <= (int) py->fpclass) { /* Reverse. */
pt = py;
py = px;
px = pt;
}
/* Now class(x) >= class(y). */
*pz = *px; /* Tentative difference: x. */
switch (pz->fpclass) {
case fp_quiet: /* NaN - x -> NaN */
case fp_signaling: /* NaN - x -> NaN */
return;
case fp_infinity: /* Inf - x -> Inf */
if (py->fpclass == fp_infinity) {
fpu_error_nan(pfpsd, pz); /* Inf - Inf -> NaN */
pz->fpclass = fp_quiet;
}
return;
case fp_zero: /* 0 - 0 -> 0 */
pz->sign = (pfpsd->fp_direction == fp_negative);
return;
default:
if (py->fpclass == fp_zero)
return;
}
/* x and y are both normal or subnormal. */
if (px->exponent < py->exponent) { /* Reverse. */
pt = py;
py = px;
px = pt;
}
/* Now exp(x) >= exp(y). */
pz->fpclass = px->fpclass;
pz->sign = px->sign;
pz->exponent = px->exponent;
pz->rounded = 0;
pz->sticky = 0;
z = pz->significand;
if (px->exponent == py->exponent) { /* no pre-alignment required */
c = 0;
c = fpu_sub3wc(&z[3], px->significand[3],
py->significand[3], c);
c = fpu_sub3wc(&z[2], px->significand[2],
py->significand[2], c);
c = fpu_sub3wc(&z[1], px->significand[1],
py->significand[1], c);
c = fpu_sub3wc(&z[0], px->significand[0],
py->significand[0], c);
if ((z[0]|z[1]|z[2]|z[3]) == 0) { /* exact zero result */
pz->sign = (pfpsd->fp_direction == fp_negative);
pz->fpclass = fp_zero;
return;
}
if (z[0] >= 0x20000) { /* sign reversal occurred */
pz->sign = py->sign;
c = 0;
c = fpu_neg2wc(&z[3], z[3], c);
c = fpu_neg2wc(&z[2], z[2], c);
c = fpu_neg2wc(&z[1], z[1], c);
c = fpu_neg2wc(&z[0], z[0], c);
}
fpu_normalize(pz);
return;
} else { /* pre-alignment required */
fpu_rightshift(py, pz->exponent - py->exponent - 1);
r = py->rounded; /* rounded bit */
s = py->sticky; /* sticky bit */
fpu_rightshift(py, 1);
g = py->rounded; /* guard bit */
if (s != 0) r = (r == 0);
if ((r|s) != 0) g = (g == 0); /* guard and rounded bits of z */
c = ((g|r|s) != 0);
c = fpu_sub3wc(&z[3], px->significand[3],
py->significand[3], c);
c = fpu_sub3wc(&z[2], px->significand[2],
py->significand[2], c);
c = fpu_sub3wc(&z[1], px->significand[1],
py->significand[1], c);
c = fpu_sub3wc(&z[0], px->significand[0],
py->significand[0], c);
if (z[0] >= 0x10000) { /* don't need post-shifted */
pz->sticky = s|r;
pz->rounded = g;
} else { /* post-shifted left 1 bit */
pz->sticky = s;
pz->rounded = r;
pz->significand[0] = (z[0]<<1)|((z[1]&0x80000000)>>31);
pz->significand[1] = (z[1]<<1)|((z[2]&0x80000000)>>31);
pz->significand[2] = (z[2]<<1)|((z[3]&0x80000000)>>31);
pz->significand[3] = (z[3]<<1)|g;
pz->exponent -= 1;
if (z[0] < 0x10000) fpu_normalize(pz);
}
return;
}
}
void
_fp_add(pfpsd, px, py, pz)
fp_simd_type *pfpsd;
unpacked *px, *py, *pz;
{
if (px->sign == py->sign)
true_add(px, py, pz);
else
true_sub(pfpsd, px, py, pz);
}
void
_fp_sub(pfpsd, px, py, pz)
fp_simd_type *pfpsd;
unpacked *px, *py, *pz;
{
if (py->fpclass < fp_quiet) py->sign = 1 - py->sign;
if (px->sign == py->sign)
true_add(px, py, pz);
else
true_sub(pfpsd, px, py, pz);
}