2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License, Version 1.0 only
2N/A * (the "License"). You may not use this file except in compliance
2N/A * with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright (c) 1994-1997, by Sun Microsystems, Inc.
2N/A * All rights reserved.
2N/A */
2N/A
2N/A#pragma ident "%Z%%M% %I% %E% SMI"
2N/A
2N/A/*
2N/A * This file contains __quad_mag_add and __quad_mag_sub, the core
2N/A * of the quad precision add and subtract operations.
2N/A */
2N/A
2N/A#include "quad.h"
2N/A
2N/A/*
2N/A * __quad_mag_add(x, y, z, fsr)
2N/A *
2N/A * Sets *z = *x + *y, rounded according to the rounding mode in *fsr,
2N/A * and updates the current exceptions in *fsr. This routine assumes
2N/A * *x and *y are finite, with the same sign (i.e., an addition of
2N/A * magnitudes), |*x| >= |*y|, and *z already has its sign bit set.
2N/A */
2N/Avoid
2N/A__quad_mag_add(const union longdouble *x, const union longdouble *y,
2N/A union longdouble *z, unsigned int *fsr)
2N/A{
2N/A unsigned int lx, ly, ex, ey, frac2, frac3, frac4;
2N/A unsigned int round, sticky, carry, rm;
2N/A int e, uflo;
2N/A
2N/A /* get the leading significand words and exponents */
2N/A ex = (x->l.msw & 0x7fffffff) >> 16;
2N/A lx = x->l.msw & 0xffff;
2N/A if (ex == 0)
2N/A ex = 1;
2N/A else
2N/A lx |= 0x10000;
2N/A
2N/A ey = (y->l.msw & 0x7fffffff) >> 16;
2N/A ly = y->l.msw & 0xffff;
2N/A if (ey == 0)
2N/A ey = 1;
2N/A else
2N/A ly |= 0x10000;
2N/A
2N/A /* prenormalize y */
2N/A e = (int) ex - (int) ey;
2N/A round = sticky = 0;
2N/A if (e >= 114) {
2N/A frac2 = x->l.frac2;
2N/A frac3 = x->l.frac3;
2N/A frac4 = x->l.frac4;
2N/A sticky = ly | y->l.frac2 | y->l.frac3 | y->l.frac4;
2N/A } else {
2N/A frac2 = y->l.frac2;
2N/A frac3 = y->l.frac3;
2N/A frac4 = y->l.frac4;
2N/A if (e >= 96) {
2N/A sticky = frac4 | frac3 | (frac2 & 0x7fffffff);
2N/A round = frac2 & 0x80000000;
2N/A frac4 = ly;
2N/A frac3 = frac2 = ly = 0;
2N/A e -= 96;
2N/A } else if (e >= 64) {
2N/A sticky = frac4 | (frac3 & 0x7fffffff);
2N/A round = frac3 & 0x80000000;
2N/A frac4 = frac2;
2N/A frac3 = ly;
2N/A frac2 = ly = 0;
2N/A e -= 64;
2N/A } else if (e >= 32) {
2N/A sticky = frac4 & 0x7fffffff;
2N/A round = frac4 & 0x80000000;
2N/A frac4 = frac3;
2N/A frac3 = frac2;
2N/A frac2 = ly;
2N/A ly = 0;
2N/A e -= 32;
2N/A }
2N/A if (e) {
2N/A sticky |= round | (frac4 & ((1 << (e - 1)) - 1));
2N/A round = frac4 & (1 << (e - 1));
2N/A frac4 = (frac4 >> e) | (frac3 << (32 - e));
2N/A frac3 = (frac3 >> e) | (frac2 << (32 - e));
2N/A frac2 = (frac2 >> e) | (ly << (32 - e));
2N/A ly >>= e;
2N/A }
2N/A
2N/A /* add, propagating carries */
2N/A frac4 += x->l.frac4;
2N/A carry = (frac4 < x->l.frac4);
2N/A frac3 += x->l.frac3;
2N/A if (carry) {
2N/A frac3++;
2N/A carry = (frac3 <= x->l.frac3);
2N/A } else {
2N/A carry = (frac3 < x->l.frac3);
2N/A }
2N/A frac2 += x->l.frac2;
2N/A if (carry) {
2N/A frac2++;
2N/A carry = (frac2 <= x->l.frac2);
2N/A } else {
2N/A carry = (frac2 < x->l.frac2);
2N/A }
2N/A lx += ly;
2N/A if (carry)
2N/A lx++;
2N/A
2N/A /* postnormalize */
2N/A if (lx >= 0x20000) {
2N/A sticky |= round;
2N/A round = frac4 & 1;
2N/A frac4 = (frac4 >> 1) | (frac3 << 31);
2N/A frac3 = (frac3 >> 1) | (frac2 << 31);
2N/A frac2 = (frac2 >> 1) | (lx << 31);
2N/A lx >>= 1;
2N/A ex++;
2N/A }
2N/A }
2N/A
2N/A /* keep track of whether the result before rounding is tiny */
2N/A uflo = (lx < 0x10000);
2N/A
2N/A /* get the rounding mode, fudging directed rounding modes */
2N/A /* as though the result were positive */
2N/A rm = *fsr >> 30;
2N/A if (z->l.msw)
2N/A rm ^= (rm >> 1);
2N/A
2N/A /* see if we need to round */
2N/A if (round | sticky) {
2N/A *fsr |= FSR_NXC;
2N/A
2N/A /* round up if necessary */
2N/A if (rm == FSR_RP || (rm == FSR_RN && round &&
2N/A (sticky || (frac4 & 1)))) {
2N/A if (++frac4 == 0)
2N/A if (++frac3 == 0)
2N/A if (++frac2 == 0)
2N/A if (++lx >= 0x20000) {
2N/A lx >>= 1;
2N/A ex++;
2N/A }
2N/A }
2N/A }
2N/A
2N/A /* check for overflow */
2N/A if (ex >= 0x7fff) {
2N/A /* store the default overflowed result */
2N/A *fsr |= FSR_OFC | FSR_NXC;
2N/A if (rm == FSR_RN || rm == FSR_RP) {
2N/A z->l.msw |= 0x7fff0000;
2N/A z->l.frac2 = z->l.frac3 = z->l.frac4 = 0;
2N/A } else {
2N/A z->l.msw |= 0x7ffeffff;
2N/A z->l.frac2 = z->l.frac3 = z->l.frac4 = 0xffffffff;
2N/A }
2N/A } else {
2N/A /* store the result */
2N/A if (lx >= 0x10000)
2N/A z->l.msw |= (ex << 16);
2N/A z->l.msw |= (lx & 0xffff);
2N/A z->l.frac2 = frac2;
2N/A z->l.frac3 = frac3;
2N/A z->l.frac4 = frac4;
2N/A
2N/A /* if the pre-rounded result was tiny and underflow trapping */
2N/A /* is enabled, simulate underflow */
2N/A if (uflo && (*fsr & FSR_UFM))
2N/A *fsr |= FSR_UFC;
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * __quad_mag_sub(x, y, z, fsr)
2N/A *
2N/A * Sets *z = *x - *y, rounded according to the rounding mode in *fsr,
2N/A * and updates the current exceptions in *fsr. This routine assumes
2N/A * *x and *y are finite, with opposite signs (i.e., a subtraction of
2N/A * magnitudes), |*x| >= |*y|, and *z already has its sign bit set.
2N/A */
2N/Avoid
2N/A__quad_mag_sub(const union longdouble *x, const union longdouble *y,
2N/A union longdouble *z, unsigned int *fsr)
2N/A{
2N/A unsigned int lx, ly, ex, ey, frac2, frac3, frac4;
2N/A unsigned int guard, round, sticky, borrow, rm;
2N/A int e;
2N/A
2N/A /* get the leading significand words and exponents */
2N/A ex = (x->l.msw & 0x7fffffff) >> 16;
2N/A lx = x->l.msw & 0xffff;
2N/A if (ex == 0)
2N/A ex = 1;
2N/A else
2N/A lx |= 0x10000;
2N/A
2N/A ey = (y->l.msw & 0x7fffffff) >> 16;
2N/A ly = y->l.msw & 0xffff;
2N/A if (ey == 0)
2N/A ey = 1;
2N/A else
2N/A ly |= 0x10000;
2N/A
2N/A /* prenormalize y */
2N/A e = (int) ex - (int) ey;
2N/A guard = round = sticky = 0;
2N/A if (e > 114) {
2N/A sticky = ly | y->l.frac2 | y->l.frac3 | y->l.frac4;
2N/A ly = frac2 = frac3 = frac4 = 0;
2N/A } else {
2N/A frac2 = y->l.frac2;
2N/A frac3 = y->l.frac3;
2N/A frac4 = y->l.frac4;
2N/A if (e >= 96) {
2N/A sticky = frac4 | frac3 | (frac2 & 0x3fffffff);
2N/A round = frac2 & 0x40000000;
2N/A guard = frac2 & 0x80000000;
2N/A frac4 = ly;
2N/A frac3 = frac2 = ly = 0;
2N/A e -= 96;
2N/A } else if (e >= 64) {
2N/A sticky = frac4 | (frac3 & 0x3fffffff);
2N/A round = frac3 & 0x40000000;
2N/A guard = frac3 & 0x80000000;
2N/A frac4 = frac2;
2N/A frac3 = ly;
2N/A frac2 = ly = 0;
2N/A e -= 64;
2N/A } else if (e >= 32) {
2N/A sticky = frac4 & 0x3fffffff;
2N/A round = frac4 & 0x40000000;
2N/A guard = frac4 & 0x80000000;
2N/A frac4 = frac3;
2N/A frac3 = frac2;
2N/A frac2 = ly;
2N/A ly = 0;
2N/A e -= 32;
2N/A }
2N/A if (e > 1) {
2N/A sticky |= guard | round |
2N/A (frac4 & ((1 << (e - 2)) - 1));
2N/A round = frac4 & (1 << (e - 2));
2N/A guard = frac4 & (1 << (e - 1));
2N/A frac4 = (frac4 >> e) | (frac3 << (32 - e));
2N/A frac3 = (frac3 >> e) | (frac2 << (32 - e));
2N/A frac2 = (frac2 >> e) | (ly << (32 - e));
2N/A ly >>= e;
2N/A } else if (e == 1) {
2N/A sticky |= round;
2N/A round = guard;
2N/A guard = frac4 & 1;
2N/A frac4 = (frac4 >> 1) | (frac3 << 31);
2N/A frac3 = (frac3 >> 1) | (frac2 << 31);
2N/A frac2 = (frac2 >> 1) | (ly << 31);
2N/A ly >>= 1;
2N/A }
2N/A }
2N/A
2N/A /* complement guard, round, and sticky as need be */
2N/A if (sticky) {
2N/A round = !round;
2N/A guard = !guard;
2N/A } else if (round) {
2N/A guard = !guard;
2N/A }
2N/A borrow = (guard | round | sticky);
2N/A
2N/A /* subtract, propagating borrows */
2N/A frac4 = x->l.frac4 - frac4;
2N/A if (borrow) {
2N/A frac4--;
2N/A borrow = (frac4 >= x->l.frac4);
2N/A } else {
2N/A borrow = (frac4 > x->l.frac4);
2N/A }
2N/A frac3 = x->l.frac3 - frac3;
2N/A if (borrow) {
2N/A frac3--;
2N/A borrow = (frac3 >= x->l.frac3);
2N/A } else {
2N/A borrow = (frac3 > x->l.frac3);
2N/A }
2N/A frac2 = x->l.frac2 - frac2;
2N/A if (borrow) {
2N/A frac2--;
2N/A borrow = (frac2 >= x->l.frac2);
2N/A } else {
2N/A borrow = (frac2 > x->l.frac2);
2N/A }
2N/A lx -= ly;
2N/A if (borrow)
2N/A lx--;
2N/A
2N/A /* get the rounding mode */
2N/A rm = *fsr >> 30;
2N/A
2N/A /* handle zero result */
2N/A if (!(lx | frac2 | frac3 | frac4 | guard)) {
2N/A z->l.msw = ((rm == FSR_RM)? 0x80000000 : 0);
2N/A z->l.frac2 = z->l.frac3 = z->l.frac4 = 0;
2N/A return;
2N/A }
2N/A
2N/A /* postnormalize */
2N/A if (lx < 0x10000) {
2N/A /* if cancellation occurred or the exponent is 1, */
2N/A /* the result is exact */
2N/A if (lx < 0x8000 || ex == 1) {
2N/A while ((lx | (frac2 & 0xfffe0000)) == 0 && ex > 32) {
2N/A lx = frac2;
2N/A frac2 = frac3;
2N/A frac3 = frac4;
2N/A frac4 = ((guard)? 0x80000000 : 0);
2N/A guard = 0;
2N/A ex -= 32;
2N/A }
2N/A while (lx < 0x10000 && ex > 1) {
2N/A lx = (lx << 1) | (frac2 >> 31);
2N/A frac2 = (frac2 << 1) | (frac3 >> 31);
2N/A frac3 = (frac3 << 1) | (frac4 >> 31);
2N/A frac4 <<= 1;
2N/A if (guard) {
2N/A frac4 |= 1;
2N/A guard = 0;
2N/A }
2N/A ex--;
2N/A }
2N/A if (lx >= 0x10000)
2N/A z->l.msw |= (ex << 16);
2N/A z->l.msw |= (lx & 0xffff);
2N/A z->l.frac2 = frac2;
2N/A z->l.frac3 = frac3;
2N/A z->l.frac4 = frac4;
2N/A
2N/A /* if the result is tiny and underflow trapping is */
2N/A /* enabled, simulate underflow */
2N/A if (lx < 0x10000 && (*fsr & FSR_UFM))
2N/A *fsr |= FSR_UFC;
2N/A return;
2N/A }
2N/A
2N/A /* otherwise we only borrowed one place */
2N/A lx = (lx << 1) | (frac2 >> 31);
2N/A frac2 = (frac2 << 1) | (frac3 >> 31);
2N/A frac3 = (frac3 << 1) | (frac4 >> 31);
2N/A frac4 <<= 1;
2N/A if (guard)
2N/A frac4 |= 1;
2N/A ex--;
2N/A } else {
2N/A sticky |= round;
2N/A round = guard;
2N/A }
2N/A
2N/A /* fudge directed rounding modes as though the result were positive */
2N/A if (z->l.msw)
2N/A rm ^= (rm >> 1);
2N/A
2N/A /* see if we need to round */
2N/A if (round | sticky) {
2N/A *fsr |= FSR_NXC;
2N/A
2N/A /* round up if necessary */
2N/A if (rm == FSR_RP || (rm == FSR_RN && round &&
2N/A (sticky || (frac4 & 1)))) {
2N/A if (++frac4 == 0)
2N/A if (++frac3 == 0)
2N/A if (++frac2 == 0)
2N/A if (++lx >= 0x20000) {
2N/A lx >>= 1;
2N/A ex++;
2N/A }
2N/A }
2N/A }
2N/A
2N/A /* store the result */
2N/A z->l.msw |= (ex << 16) | (lx & 0xffff);
2N/A z->l.frac2 = frac2;
2N/A z->l.frac3 = frac3;
2N/A z->l.frac4 = frac4;
2N/A}