/* * 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) 1994-1997, by Sun Microsystems, Inc. * All rights reserved. */ /* * This file contains __quad_mag_add and __quad_mag_sub, the core * of the quad precision add and subtract operations. */ #include "quad.h" /* * __quad_mag_add(x, y, z, fsr) * * Sets *z = *x + *y, rounded according to the rounding mode in *fsr, * and updates the current exceptions in *fsr. This routine assumes * *x and *y are finite, with the same sign (i.e., an addition of * magnitudes), |*x| >= |*y|, and *z already has its sign bit set. */ void __quad_mag_add(const union longdouble *x, const union longdouble *y, union longdouble *z, unsigned int *fsr) { unsigned int lx, ly, ex, ey, frac2, frac3, frac4; unsigned int round, sticky, carry, rm; int e, uflo; /* get the leading significand words and exponents */ ex = (x->l.msw & 0x7fffffff) >> 16; lx = x->l.msw & 0xffff; if (ex == 0) ex = 1; else lx |= 0x10000; ey = (y->l.msw & 0x7fffffff) >> 16; ly = y->l.msw & 0xffff; if (ey == 0) ey = 1; else ly |= 0x10000; /* prenormalize y */ e = (int) ex - (int) ey; round = sticky = 0; if (e >= 114) { frac2 = x->l.frac2; frac3 = x->l.frac3; frac4 = x->l.frac4; sticky = ly | y->l.frac2 | y->l.frac3 | y->l.frac4; } else { frac2 = y->l.frac2; frac3 = y->l.frac3; frac4 = y->l.frac4; if (e >= 96) { sticky = frac4 | frac3 | (frac2 & 0x7fffffff); round = frac2 & 0x80000000; frac4 = ly; frac3 = frac2 = ly = 0; e -= 96; } else if (e >= 64) { sticky = frac4 | (frac3 & 0x7fffffff); round = frac3 & 0x80000000; frac4 = frac2; frac3 = ly; frac2 = ly = 0; e -= 64; } else if (e >= 32) { sticky = frac4 & 0x7fffffff; round = frac4 & 0x80000000; frac4 = frac3; frac3 = frac2; frac2 = ly; ly = 0; e -= 32; } if (e) { sticky |= round | (frac4 & ((1 << (e - 1)) - 1)); round = frac4 & (1 << (e - 1)); frac4 = (frac4 >> e) | (frac3 << (32 - e)); frac3 = (frac3 >> e) | (frac2 << (32 - e)); frac2 = (frac2 >> e) | (ly << (32 - e)); ly >>= e; } /* add, propagating carries */ frac4 += x->l.frac4; carry = (frac4 < x->l.frac4); frac3 += x->l.frac3; if (carry) { frac3++; carry = (frac3 <= x->l.frac3); } else { carry = (frac3 < x->l.frac3); } frac2 += x->l.frac2; if (carry) { frac2++; carry = (frac2 <= x->l.frac2); } else { carry = (frac2 < x->l.frac2); } lx += ly; if (carry) lx++; /* postnormalize */ if (lx >= 0x20000) { sticky |= round; round = frac4 & 1; frac4 = (frac4 >> 1) | (frac3 << 31); frac3 = (frac3 >> 1) | (frac2 << 31); frac2 = (frac2 >> 1) | (lx << 31); lx >>= 1; ex++; } } /* keep track of whether the result before rounding is tiny */ uflo = (lx < 0x10000); /* get the rounding mode, fudging directed rounding modes */ /* as though the result were positive */ rm = *fsr >> 30; if (z->l.msw) rm ^= (rm >> 1); /* see if we need to round */ if (round | sticky) { *fsr |= FSR_NXC; /* round up if necessary */ if (rm == FSR_RP || (rm == FSR_RN && round && (sticky || (frac4 & 1)))) { if (++frac4 == 0) if (++frac3 == 0) if (++frac2 == 0) if (++lx >= 0x20000) { lx >>= 1; ex++; } } } /* check for overflow */ if (ex >= 0x7fff) { /* store the default overflowed result */ *fsr |= FSR_OFC | FSR_NXC; if (rm == FSR_RN || rm == FSR_RP) { z->l.msw |= 0x7fff0000; z->l.frac2 = z->l.frac3 = z->l.frac4 = 0; } else { z->l.msw |= 0x7ffeffff; z->l.frac2 = z->l.frac3 = z->l.frac4 = 0xffffffff; } } else { /* store the result */ if (lx >= 0x10000) z->l.msw |= (ex << 16); z->l.msw |= (lx & 0xffff); z->l.frac2 = frac2; z->l.frac3 = frac3; z->l.frac4 = frac4; /* if the pre-rounded result was tiny and underflow trapping */ /* is enabled, simulate underflow */ if (uflo && (*fsr & FSR_UFM)) *fsr |= FSR_UFC; } } /* * __quad_mag_sub(x, y, z, fsr) * * Sets *z = *x - *y, rounded according to the rounding mode in *fsr, * and updates the current exceptions in *fsr. This routine assumes * *x and *y are finite, with opposite signs (i.e., a subtraction of * magnitudes), |*x| >= |*y|, and *z already has its sign bit set. */ void __quad_mag_sub(const union longdouble *x, const union longdouble *y, union longdouble *z, unsigned int *fsr) { unsigned int lx, ly, ex, ey, frac2, frac3, frac4; unsigned int guard, round, sticky, borrow, rm; int e; /* get the leading significand words and exponents */ ex = (x->l.msw & 0x7fffffff) >> 16; lx = x->l.msw & 0xffff; if (ex == 0) ex = 1; else lx |= 0x10000; ey = (y->l.msw & 0x7fffffff) >> 16; ly = y->l.msw & 0xffff; if (ey == 0) ey = 1; else ly |= 0x10000; /* prenormalize y */ e = (int) ex - (int) ey; guard = round = sticky = 0; if (e > 114) { sticky = ly | y->l.frac2 | y->l.frac3 | y->l.frac4; ly = frac2 = frac3 = frac4 = 0; } else { frac2 = y->l.frac2; frac3 = y->l.frac3; frac4 = y->l.frac4; if (e >= 96) { sticky = frac4 | frac3 | (frac2 & 0x3fffffff); round = frac2 & 0x40000000; guard = frac2 & 0x80000000; frac4 = ly; frac3 = frac2 = ly = 0; e -= 96; } else if (e >= 64) { sticky = frac4 | (frac3 & 0x3fffffff); round = frac3 & 0x40000000; guard = frac3 & 0x80000000; frac4 = frac2; frac3 = ly; frac2 = ly = 0; e -= 64; } else if (e >= 32) { sticky = frac4 & 0x3fffffff; round = frac4 & 0x40000000; guard = frac4 & 0x80000000; frac4 = frac3; frac3 = frac2; frac2 = ly; ly = 0; e -= 32; } if (e > 1) { sticky |= guard | round | (frac4 & ((1 << (e - 2)) - 1)); round = frac4 & (1 << (e - 2)); guard = frac4 & (1 << (e - 1)); frac4 = (frac4 >> e) | (frac3 << (32 - e)); frac3 = (frac3 >> e) | (frac2 << (32 - e)); frac2 = (frac2 >> e) | (ly << (32 - e)); ly >>= e; } else if (e == 1) { sticky |= round; round = guard; guard = frac4 & 1; frac4 = (frac4 >> 1) | (frac3 << 31); frac3 = (frac3 >> 1) | (frac2 << 31); frac2 = (frac2 >> 1) | (ly << 31); ly >>= 1; } } /* complement guard, round, and sticky as need be */ if (sticky) { round = !round; guard = !guard; } else if (round) { guard = !guard; } borrow = (guard | round | sticky); /* subtract, propagating borrows */ frac4 = x->l.frac4 - frac4; if (borrow) { frac4--; borrow = (frac4 >= x->l.frac4); } else { borrow = (frac4 > x->l.frac4); } frac3 = x->l.frac3 - frac3; if (borrow) { frac3--; borrow = (frac3 >= x->l.frac3); } else { borrow = (frac3 > x->l.frac3); } frac2 = x->l.frac2 - frac2; if (borrow) { frac2--; borrow = (frac2 >= x->l.frac2); } else { borrow = (frac2 > x->l.frac2); } lx -= ly; if (borrow) lx--; /* get the rounding mode */ rm = *fsr >> 30; /* handle zero result */ if (!(lx | frac2 | frac3 | frac4 | guard)) { z->l.msw = ((rm == FSR_RM)? 0x80000000 : 0); z->l.frac2 = z->l.frac3 = z->l.frac4 = 0; return; } /* postnormalize */ if (lx < 0x10000) { /* if cancellation occurred or the exponent is 1, */ /* the result is exact */ if (lx < 0x8000 || ex == 1) { while ((lx | (frac2 & 0xfffe0000)) == 0 && ex > 32) { lx = frac2; frac2 = frac3; frac3 = frac4; frac4 = ((guard)? 0x80000000 : 0); guard = 0; ex -= 32; } while (lx < 0x10000 && ex > 1) { lx = (lx << 1) | (frac2 >> 31); frac2 = (frac2 << 1) | (frac3 >> 31); frac3 = (frac3 << 1) | (frac4 >> 31); frac4 <<= 1; if (guard) { frac4 |= 1; guard = 0; } ex--; } if (lx >= 0x10000) z->l.msw |= (ex << 16); z->l.msw |= (lx & 0xffff); z->l.frac2 = frac2; z->l.frac3 = frac3; z->l.frac4 = frac4; /* if the result is tiny and underflow trapping is */ /* enabled, simulate underflow */ if (lx < 0x10000 && (*fsr & FSR_UFM)) *fsr |= FSR_UFC; return; } /* otherwise we only borrowed one place */ lx = (lx << 1) | (frac2 >> 31); frac2 = (frac2 << 1) | (frac3 >> 31); frac3 = (frac3 << 1) | (frac4 >> 31); frac4 <<= 1; if (guard) frac4 |= 1; ex--; } else { sticky |= round; round = guard; } /* fudge directed rounding modes as though the result were positive */ if (z->l.msw) rm ^= (rm >> 1); /* see if we need to round */ if (round | sticky) { *fsr |= FSR_NXC; /* round up if necessary */ if (rm == FSR_RP || (rm == FSR_RN && round && (sticky || (frac4 & 1)))) { if (++frac4 == 0) if (++frac3 == 0) if (++frac2 == 0) if (++lx >= 0x20000) { lx >>= 1; ex++; } } } /* store the result */ z->l.msw |= (ex << 16) | (lx & 0xffff); z->l.frac2 = frac2; z->l.frac3 = frac3; z->l.frac4 = frac4; }