diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1997-01-07 02:33:00 +0000 |
---|---|---|
committer | <ralf@linux-mips.org> | 1997-01-07 02:33:00 +0000 |
commit | beb116954b9b7f3bb56412b2494b562f02b864b1 (patch) | |
tree | 120e997879884e1b9d93b265221b939d2ef1ade1 /arch/alpha/math-emu/ieee-math.c | |
parent | 908d4681a1dc3792ecafbe64265783a86c4cccb6 (diff) |
Import of Linux/MIPS 2.1.14
Diffstat (limited to 'arch/alpha/math-emu/ieee-math.c')
-rw-r--r-- | arch/alpha/math-emu/ieee-math.c | 1343 |
1 files changed, 1343 insertions, 0 deletions
diff --git a/arch/alpha/math-emu/ieee-math.c b/arch/alpha/math-emu/ieee-math.c new file mode 100644 index 000000000..d408eece5 --- /dev/null +++ b/arch/alpha/math-emu/ieee-math.c @@ -0,0 +1,1343 @@ +/* + * ieee-math.c - IEEE floating point emulation code + * Copyright (C) 1989,1990,1991,1995 by + * Digital Equipment Corporation, Maynard, Massachusetts. + * + * Heavily modified for Linux/Alpha. Changes are Copyright (c) 1995 + * by David Mosberger (davidm@azstarnet.com). + * + * This file may be redistributed according to the terms of the + * GNU General Public License. + */ +/* + * The original code did not have any comments. I have created many + * comments as I fix the bugs in the code. My comments are based on + * my observation and interpretation of the code. If the original + * author would have spend a few minutes to comment the code, we would + * never had a problem of misinterpretation. -HA + * + * This code could probably be a lot more optimized (especially the + * division routine). However, my foremost concern was to get the + * IEEE behavior right. Performance is less critical as these + * functions are used on exceptional numbers only (well, assuming you + * don't turn on the "trap on inexact"...). + */ +#include "ieee-math.h" + +#define STICKY_S 0x20000000 /* both in longword 0 of fraction */ +#define STICKY_T 1 + +/* + * Careful: order matters here! + */ +enum { + NaN, QNaN, INFTY, ZERO, DENORM, NORMAL +}; + +enum { + SINGLE, DOUBLE +}; + +typedef unsigned long fpclass_t; + +#define IEEE_TMAX 0x7fefffffffffffff +#define IEEE_SMAX 0x47efffffe0000000 +#define IEEE_SNaN 0xfff00000000f0000 +#define IEEE_QNaN 0xfff8000000000000 +#define IEEE_PINF 0x7ff0000000000000 +#define IEEE_NINF 0xfff0000000000000 + + +/* + * The memory format of S floating point numbers differs from the + * register format. In the following, the bitnumbers above the + * diagram below give the memory format while the numbers below give + * the register format. + * + * 31 30 23 22 0 + * +-----------------------------------------------+ + * S | s | exp | fraction | + * +-----------------------------------------------+ + * 63 62 52 51 29 + * + * For T floating point numbers, the register and memory formats + * match: + * + * +-------------------------------------------------------------------+ + * T | s | exp | frac | tion | + * +-------------------------------------------------------------------+ + * 63 62 52 51 32 31 0 + */ +typedef struct { + unsigned long f[2]; /* bit 55 in f[0] is the factor of 2^0*/ + int s; /* 1 bit sign (0 for +, 1 for -) */ + int e; /* 16 bit signed exponent */ +} EXTENDED; + + +/* + * Return the sign of a Q integer, S or T fp number in the register + * format. + */ +static inline int +sign (unsigned long a) +{ + if ((long) a < 0) + return -1; + else + return 1; +} + + +static inline long +cmp128 (const long a[2], const long b[2]) +{ + if (a[1] < b[1]) return -1; + if (a[1] > b[1]) return 1; + return a[0] - b[0]; +} + + +static inline void +sll128 (unsigned long a[2]) +{ + a[1] = (a[1] << 1) | (a[0] >> 63); + a[0] <<= 1; +} + + +static inline void +srl128 (unsigned long a[2]) +{ + a[0] = (a[0] >> 1) | (a[1] << 63); + a[1] >>= 1; +} + + +static inline void +add128 (const unsigned long a[2], const unsigned long b[2], unsigned long c[2]) +{ + unsigned long carry = a[0] > (0xffffffffffffffff - b[0]); + + c[0] = a[0] + b[0]; + c[1] = a[1] + b[1] + carry; +} + + +static inline void +sub128 (const unsigned long a[2], const unsigned long b[2], unsigned long c[2]) +{ + unsigned long borrow = a[0] < b[0]; + + c[0] = a[0] - b[0]; + c[1] = a[1] - b[1] - borrow; +} + + +static inline void +mul64 (const unsigned long a, const unsigned long b, unsigned long c[2]) +{ + asm ("mulq %2,%3,%0\n\t" + "umulh %2,%3,%1" + : "r="(c[0]), "r="(c[1]) : "r"(a), "r"(b)); +} + + +static void +div128 (unsigned long a[2], unsigned long b[2], unsigned long c[2]) +{ + unsigned long mask[2] = {1, 0}; + + /* + * Shift b until either the sign bit is set or until it is at + * least as big as the dividend: + */ + while (cmp128(b, a) < 0 && sign(b[1]) >= 0) { + sll128(b); + sll128(mask); + } + c[0] = c[1] = 0; + do { + if (cmp128(a, b) >= 0) { + sub128(a, b, a); + add128(mask, c, c); + } + srl128(mask); + srl128(b); + } while (mask[0] || mask[1]); +} + + +static void +normalize (EXTENDED *a) +{ + if (!a->f[0] && !a->f[1]) + return; /* zero fraction, unnormalizable... */ + /* + * In "extended" format, the "1" in "1.f" is explicit; it is + * in bit 55 of f[0], and the decimal point is understood to + * be between bit 55 and bit 54. To normalize, shift the + * fraction until we have a "1" in bit 55. + */ + if ((a->f[0] & 0xff00000000000000) != 0 || a->f[1] != 0) { + /* + * Mantissa is greater than 1.0: + */ + while ((a->f[0] & 0xff80000000000000) != 0x0080000000000000 || + a->f[1] != 0) + { + unsigned long sticky; + + ++a->e; + sticky = a->f[0] & 1; + srl128(a->f); + a->f[0] |= sticky; + } + return; + } + + if (!(a->f[0] & 0x0080000000000000)) { + /* + * Mantissa is less than 1.0: + */ + while (!(a->f[0] & 0x0080000000000000)) { + --a->e; + a->f[0] <<= 1; + } + return; + } +} + + +static inline fpclass_t +ieee_fpclass (unsigned long a) +{ + unsigned long exp, fract; + + exp = (a >> 52) & 0x7ff; /* 11 bits of exponent */ + fract = a & 0x000fffffffffffff; /* 52 bits of fraction */ + if (exp == 0) { + if (fract == 0) + return ZERO; + return DENORM; + } + if (exp == 0x7ff) { + if (fract == 0) + return INFTY; + if (((fract >> 51) & 1) != 0) + return QNaN; + return NaN; + } + return NORMAL; +} + + +/* + * Translate S/T fp number in register format into extended format. + */ +static fpclass_t +extend_ieee (unsigned long a, EXTENDED *b, int prec) +{ + fpclass_t result_kind; + + b->s = a >> 63; + b->e = ((a >> 52) & 0x7ff) - 0x3ff; /* remove bias */ + b->f[1] = 0; + /* + * We shift f[1] left three bits so that the higher order bits + * of the fraction will reside in bits 55 through 0 of f[0]. + */ + b->f[0] = (a & 0x000fffffffffffff) << 3; + result_kind = ieee_fpclass(a); + if (result_kind == NORMAL) { + /* set implied 1. bit: */ + b->f[0] |= 1UL << 55; + } else if (result_kind == DENORM) { + if (prec == SINGLE) + b->e = -126; + else + b->e = -1022; + } + return result_kind; +} + + +/* + * INPUT PARAMETERS: + * a a number in EXTENDED format to be converted to + * s-floating format. + * f rounding mode and exception enable bits. + * OUTPUT PARAMETERS: + * b will contain the s-floating number that "a" was + * converted to (in register format). + */ +static unsigned long +make_s_ieee (long f, EXTENDED *a, unsigned long *b) +{ + unsigned long res, sticky; + + if (!a->f[0] && !a->f[1]) { + *b = (unsigned long) a->s << 63; /* return +/-0 */ + return 0; + } + + normalize(a); + res = 0; + + if (a->e < -0x7e) { + res = FPCR_INE; + if (f & IEEE_TRAP_ENABLE_UNF) { + res |= FPCR_UNF; + a->e += 0xc0; /* scale up result by 2^alpha */ + } else { + /* try making denormalized number: */ + while (a->e < -0x7e) { + ++a->e; + sticky = a->f[0] & 1; + srl128(a->f); + if (!a->f[0] && !a->f[0]) { + /* underflow: replace with exact 0 */ + res |= FPCR_UNF; + break; + } + a->f[0] |= sticky; + } + a->e = -0x3ff; + } + } + if (a->e >= 0x80) { + res = FPCR_OVF | FPCR_INE; + if (f & IEEE_TRAP_ENABLE_OVF) { + a->e -= 0xc0; /* scale down result by 2^alpha */ + } else { + /* + * Overflow without trap enabled, substitute + * result according to rounding mode: + */ + switch (RM(f)) { + case ROUND_NEAR: + *b = IEEE_PINF; + break; + + case ROUND_CHOP: + *b = IEEE_SMAX; + break; + + case ROUND_NINF: + if (a->s) { + *b = IEEE_PINF; + } else { + *b = IEEE_SMAX; + } + break; + + case ROUND_PINF: + if (a->s) { + *b = IEEE_SMAX; + } else { + *b = IEEE_PINF; + } + break; + } + *b |= ((unsigned long) a->s << 63); + return res; + } + } + + *b = (((unsigned long) a->s << 63) | + (((unsigned long) a->e + 0x3ff) << 52) | + ((a->f[0] >> 3) & 0x000fffffe0000000)); + return res; +} + + +static unsigned long +make_t_ieee (long f, EXTENDED *a, unsigned long *b) +{ + unsigned long res, sticky; + + if (!a->f[0] && !a->f[1]) { + *b = (unsigned long) a->s << 63; /* return +/-0 */ + return 0; + } + + normalize(a); + res = 0; + if (a->e < -0x3fe) { + res = FPCR_INE; + if (f & IEEE_TRAP_ENABLE_UNF) { + res |= FPCR_UNF; + a->e += 0x600; + } else { + /* try making denormalized number: */ + while (a->e < -0x3fe) { + ++a->e; + sticky = a->f[0] & 1; + srl128(a->f); + if (!a->f[0] && !a->f[0]) { + /* underflow: replace with exact 0 */ + res |= FPCR_UNF; + break; + } + a->f[0] |= sticky; + } + a->e = -0x3ff; + } + } + if (a->e > 0x3ff) { + res = FPCR_OVF | FPCR_INE; + if (f & IEEE_TRAP_ENABLE_OVF) { + a->e -= 0x600; /* scale down result by 2^alpha */ + } else { + /* + * Overflow without trap enabled, substitute + * result according to rounding mode: + */ + switch (RM(f)) { + case ROUND_NEAR: + *b = IEEE_PINF; + break; + + case ROUND_CHOP: + *b = IEEE_TMAX; + break; + + case ROUND_NINF: + if (a->s) { + *b = IEEE_PINF; + } else { + *b = IEEE_TMAX; + } + break; + + case ROUND_PINF: + if (a->s) { + *b = IEEE_TMAX; + } else { + *b = IEEE_PINF; + } + break; + } + *b |= ((unsigned long) a->s << 63); + return res; + } + } + *b = (((unsigned long) a->s << 63) | + (((unsigned long) a->e + 0x3ff) << 52) | + ((a->f[0] >> 3) & 0x000fffffffffffff)); + return res; +} + + +/* + * INPUT PARAMETERS: + * a EXTENDED format number to be rounded. + * rm integer with value ROUND_NEAR, ROUND_CHOP, etc. + * indicates how "a" should be rounded to produce "b". + * OUTPUT PARAMETERS: + * b s-floating number produced by rounding "a". + * RETURN VALUE: + * if no errors occurred, will be zero. Else will contain flags + * like FPCR_INE_OP, etc. + */ +static unsigned long +round_s_ieee (int f, EXTENDED *a, unsigned long *b) +{ + unsigned long diff1, diff2, res = 0; + EXTENDED z1, z2; + + if (!(a->f[0] & 0xffffffff)) { + return make_s_ieee(f, a, b); /* no rounding error */ + } + + /* + * z1 and z2 are the S-floating numbers with the next smaller/greater + * magnitude than a, respectively. + */ + z1.s = z2.s = a->s; + z1.e = z2.e = a->e; + z1.f[0] = z2.f[0] = a->f[0] & 0xffffffff00000000; + z1.f[1] = z2.f[1] = 0; + z2.f[0] += 0x100000000; /* next bigger S float number */ + + switch (RM(f)) { + case ROUND_NEAR: + diff1 = a->f[0] - z1.f[0]; + diff2 = z2.f[0] - a->f[0]; + if (diff1 > diff2) + res = make_s_ieee(f, &z2, b); + else if (diff2 > diff1) + res = make_s_ieee(f, &z1, b); + else + /* equal distance: round towards even */ + if (z1.f[0] & 0x100000000) + res = make_s_ieee(f, &z2, b); + else + res = make_s_ieee(f, &z1, b); + break; + + case ROUND_CHOP: + res = make_s_ieee(f, &z1, b); + break; + + case ROUND_PINF: + if (a->s) { + res = make_s_ieee(f, &z1, b); + } else { + res = make_s_ieee(f, &z2, b); + } + break; + + case ROUND_NINF: + if (a->s) { + res = make_s_ieee(f, &z2, b); + } else { + res = make_s_ieee(f, &z1, b); + } + break; + } + return FPCR_INE | res; +} + + +static unsigned long +round_t_ieee (int f, EXTENDED *a, unsigned long *b) +{ + unsigned long diff1, diff2, res; + EXTENDED z1, z2; + + if (!(a->f[0] & 0x7)) { + /* no rounding error */ + return make_t_ieee(f, a, b); + } + + z1.s = z2.s = a->s; + z1.e = z2.e = a->e; + z1.f[0] = z2.f[0] = a->f[0] & ~0x7; + z1.f[1] = z2.f[1] = 0; + z2.f[0] += (1 << 3); + + res = 0; + switch (RM(f)) { + case ROUND_NEAR: + diff1 = a->f[0] - z1.f[0]; + diff2 = z2.f[0] - a->f[0]; + if (diff1 > diff2) + res = make_t_ieee(f, &z2, b); + else if (diff2 > diff1) + res = make_t_ieee(f, &z1, b); + else + /* equal distance: round towards even */ + if (z1.f[0] & (1 << 3)) + res = make_t_ieee(f, &z2, b); + else + res = make_t_ieee(f, &z1, b); + break; + + case ROUND_CHOP: + res = make_t_ieee(f, &z1, b); + break; + + case ROUND_PINF: + if (a->s) { + res = make_t_ieee(f, &z1, b); + } else { + res = make_t_ieee(f, &z2, b); + } + break; + + case ROUND_NINF: + if (a->s) { + res = make_t_ieee(f, &z2, b); + } else { + res = make_t_ieee(f, &z1, b); + } + break; + } + return FPCR_INE | res; +} + + +static fpclass_t +add_kernel_ieee (EXTENDED *op_a, EXTENDED *op_b, EXTENDED *op_c) +{ + unsigned long mask, fa, fb, fc; + int diff; + + diff = op_a->e - op_b->e; + fa = op_a->f[0]; + fb = op_b->f[0]; + if (diff < 0) { + diff = -diff; + op_c->e = op_b->e; + mask = (1UL << diff) - 1; + fa >>= diff; + if (op_a->f[0] & mask) { + fa |= 1; /* set sticky bit */ + } + } else { + op_c->e = op_a->e; + mask = (1UL << diff) - 1; + fb >>= diff; + if (op_b->f[0] & mask) { + fb |= 1; /* set sticky bit */ + } + } + if (op_a->s) + fa = -fa; + if (op_b->s) + fb = -fb; + fc = fa + fb; + op_c->f[1] = 0; + op_c->s = fc >> 63; + if (op_c->s) { + fc = -fc; + } + op_c->f[0] = fc; + normalize(op_c); + return 0; +} + + +/* + * converts s-floating "a" to t-floating "b". + * + * INPUT PARAMETERS: + * a a s-floating number to be converted + * f the rounding mode (ROUND_NEAR, etc. ) + * OUTPUT PARAMETERS: + * b the t-floating number that "a" is converted to. + * RETURN VALUE: + * error flags - i.e., zero if no errors occurred, + * FPCR_INV if invalid operation occurred, etc. + */ +unsigned long +ieee_CVTST (int f, unsigned long a, unsigned long *b) +{ + EXTENDED temp; + fpclass_t a_type; + + a_type = extend_ieee(a, &temp, SINGLE); + if (a_type >= NaN && a_type <= INFTY) { + *b = a; + if (a_type == NaN) { + *b |= (1UL << 51); /* turn SNaN into QNaN */ + return FPCR_INV; + } + return 0; + } + return round_t_ieee(f, &temp, b); +} + + +/* + * converts t-floating "a" to s-floating "b". + * + * INPUT PARAMETERS: + * a a t-floating number to be converted + * f the rounding mode (ROUND_NEAR, etc. ) + * OUTPUT PARAMETERS: + * b the s-floating number that "a" is converted to. + * RETURN VALUE: + * error flags - i.e., zero if no errors occurred, + * FPCR_INV if invalid operation occurred, etc. + */ +unsigned long +ieee_CVTTS (int f, unsigned long a, unsigned long *b) +{ + EXTENDED temp; + fpclass_t a_type; + + a_type = extend_ieee(a, &temp, DOUBLE); + if (a_type >= NaN && a_type <= INFTY) { + *b = a; + if (a_type == NaN) { + *b |= (1UL << 51); /* turn SNaN into QNaN */ + return FPCR_INV; + } + return 0; + } + return round_s_ieee(f, &temp, b); +} + + +/* + * converts q-format (64-bit integer) "a" to s-floating "b". + * + * INPUT PARAMETERS: + * a an 64-bit integer to be converted. + * f the rounding mode (ROUND_NEAR, etc. ) + * OUTPUT PARAMETERS: + * b the s-floating number "a" is converted to. + * RETURN VALUE: + * error flags - i.e., zero if no errors occurred, + * FPCR_INV if invalid operation occurred, etc. + */ +unsigned long +ieee_CVTQS (int f, unsigned long a, unsigned long *b) +{ + EXTENDED op_b; + + op_b.s = 0; + op_b.f[0] = a; + op_b.f[1] = 0; + if (sign(a) < 0) { + op_b.s = 1; + op_b.f[0] = -a; + } + op_b.e = 55; + normalize(&op_b); + return round_s_ieee(f, &op_b, b); +} + + +/* + * converts 64-bit integer "a" to t-floating "b". + * + * INPUT PARAMETERS: + * a a 64-bit integer to be converted. + * f the rounding mode (ROUND_NEAR, etc.) + * OUTPUT PARAMETERS: + * b the t-floating number "a" is converted to. + * RETURN VALUE: + * error flags - i.e., zero if no errors occurred, + * FPCR_INV if invalid operation occurred, etc. + */ +unsigned long +ieee_CVTQT (int f, unsigned long a, unsigned long *b) +{ + EXTENDED op_b; + + op_b.s = 0; + op_b.f[0] = a; + op_b.f[1] = 0; + if (sign(a) < 0) { + op_b.s = 1; + op_b.f[0] = -a; + } + op_b.e = 55; + normalize(&op_b); + return round_t_ieee(f, &op_b, b); +} + + +/* + * converts t-floating "a" to 64-bit integer (q-format) "b". + * + * INPUT PARAMETERS: + * a a t-floating number to be converted. + * f the rounding mode (ROUND_NEAR, etc. ) + * OUTPUT PARAMETERS: + * b the 64-bit integer "a" is converted to. + * RETURN VALUE: + * error flags - i.e., zero if no errors occurred, + * FPCR_INV if invalid operation occurred, etc. + */ +unsigned long +ieee_CVTTQ (int f, unsigned long a, unsigned long *b) +{ + unsigned int midway; + unsigned long ov, uv, res = 0; + fpclass_t a_type; + EXTENDED temp; + + *b = 0; + a_type = extend_ieee(a, &temp, DOUBLE); + if (a_type == NaN || a_type == INFTY) + return FPCR_INV; + if (a_type == QNaN) + return 0; + + if (temp.e > 0) { + ov = 0; + while (temp.e > 0) { + --temp.e; + ov |= temp.f[1] >> 63; + sll128(temp.f); + } + if (ov || (temp.f[1] & 0xffc0000000000000)) + res |= FPCR_IOV | FPCR_INE; + } + if (temp.e < 0) { + while (temp.e < 0) { + ++temp.e; + uv = temp.f[0] & 1; /* save sticky bit */ + srl128(temp.f); + temp.f[0] |= uv; + } + } + *b = ((temp.f[1] << 9) | (temp.f[0] >> 55)) & 0x7fffffffffffffff; + /* + * Notice: the fraction is only 52 bits long. Thus, rounding + * cannot possibly result in an integer overflow. + */ + switch (RM(f)) { + case ROUND_NEAR: + if (temp.f[0] & 0x0040000000000000) { + midway = (temp.f[0] & 0x003fffffffffffff) == 0; + if ((midway && (temp.f[0] & 0x0080000000000000)) || + !midway) + ++b; + } + break; + + case ROUND_PINF: + if ((temp.f[0] & 0x003fffffffffffff) != 0) + ++b; + break; + + case ROUND_NINF: + if ((temp.f[0] & 0x003fffffffffffff) != 0) + --b; + break; + + case ROUND_CHOP: + /* no action needed */ + break; + } + if ((temp.f[0] & 0x003fffffffffffff) != 0) + res |= FPCR_INE; + + if (temp.s) { + *b = -*b; + } + return res; +} + + +unsigned long +ieee_CMPTEQ (unsigned long a, unsigned long b, unsigned long *c) +{ + EXTENDED op_a, op_b; + fpclass_t a_type, b_type; + + *c = 0; + a_type = extend_ieee(a, &op_a, DOUBLE); + b_type = extend_ieee(b, &op_b, DOUBLE); + if (a_type == NaN || b_type == NaN) + return FPCR_INV; + if (a_type == QNaN || b_type == QNaN) + return 0; + + if ((op_a.e == op_b.e && op_a.s == op_b.s && + op_a.f[0] == op_b.f[0] && op_a.f[1] == op_b.f[1]) || + (a_type == ZERO && b_type == ZERO)) + *c = 0x4000000000000000; + return 0; +} + + +unsigned long +ieee_CMPTLT (unsigned long a, unsigned long b, unsigned long *c) +{ + fpclass_t a_type, b_type; + EXTENDED op_a, op_b; + + *c = 0; + a_type = extend_ieee(a, &op_a, DOUBLE); + b_type = extend_ieee(b, &op_b, DOUBLE); + if (a_type == NaN || b_type == NaN) + return FPCR_INV; + if (a_type == QNaN || b_type == QNaN) + return 0; + + if ((op_a.s == 1 && op_b.s == 0 && + (a_type != ZERO || b_type != ZERO)) || + (op_a.s == 1 && op_b.s == 1 && + (op_a.e > op_b.e || (op_a.e == op_b.e && + cmp128(op_a.f, op_b.f) > 0))) || + (op_a.s == 0 && op_b.s == 0 && + (op_a.e < op_b.e || (op_a.e == op_b.e && + cmp128(op_a.f,op_b.f) < 0)))) + *c = 0x4000000000000000; + return 0; +} + + +unsigned long +ieee_CMPTLE (unsigned long a, unsigned long b, unsigned long *c) +{ + fpclass_t a_type, b_type; + EXTENDED op_a, op_b; + + *c = 0; + a_type = extend_ieee(a, &op_a, DOUBLE); + b_type = extend_ieee(b, &op_b, DOUBLE); + if (a_type == NaN || b_type == NaN) + return FPCR_INV; + if (a_type == QNaN || b_type == QNaN) + return 0; + + if ((a_type == ZERO && b_type == ZERO) || + (op_a.s == 1 && op_b.s == 0) || + (op_a.s == 1 && op_b.s == 1 && + (op_a.e > op_b.e || (op_a.e == op_b.e && + cmp128(op_a.f,op_b.f) >= 0))) || + (op_a.s == 0 && op_b.s == 0 && + (op_a.e < op_b.e || (op_a.e == op_b.e && + cmp128(op_a.f,op_b.f) <= 0)))) + *c = 0x4000000000000000; + return 0; +} + + +unsigned long +ieee_CMPTUN (unsigned long a, unsigned long b, unsigned long *c) +{ + fpclass_t a_type, b_type; + EXTENDED op_a, op_b; + + *c = 0x4000000000000000; + a_type = extend_ieee(a, &op_a, DOUBLE); + b_type = extend_ieee(b, &op_b, DOUBLE); + if (a_type == NaN || b_type == NaN) + return FPCR_INV; + if (a_type == QNaN || b_type == QNaN) + return 0; + *c = 0; + return 0; +} + + +/* + * Add a + b = c, where a, b, and c are ieee s-floating numbers. "f" + * contains the rounding mode etc. + */ +unsigned long +ieee_ADDS (int f, unsigned long a, unsigned long b, unsigned long *c) +{ + fpclass_t a_type, b_type; + EXTENDED op_a, op_b, op_c; + + a_type = extend_ieee(a, &op_a, SINGLE); + b_type = extend_ieee(b, &op_b, SINGLE); + if ((a_type >= NaN && a_type <= INFTY) || + (b_type >= NaN && b_type <= INFTY)) + { + /* propagate NaNs according to arch. ref. handbook: */ + if (b_type == QNaN) + *c = b; + else if (b_type == NaN) + *c = b | (1UL << 51); + else if (a_type == QNaN) + *c = a; + else if (a_type == NaN) + *c = a | (1UL << 51); + + if (a_type == NaN || b_type == NaN) + return FPCR_INV; + if (a_type == QNaN || b_type == QNaN) + return 0; + + if (a_type == INFTY && b_type == INFTY && sign(a) != sign(b)) { + *c = IEEE_QNaN; + return FPCR_INV; + } + if (a_type == INFTY) + *c = a; + else + *c = b; + return 0; + } + + add_kernel_ieee(&op_a, &op_b, &op_c); + /* special case for -0 + -0 ==> -0 */ + if (a_type == ZERO && b_type == ZERO) + op_c.s = op_a.s && op_b.s; + return round_s_ieee(f, &op_c, c); +} + + +/* + * Add a + b = c, where a, b, and c are ieee t-floating numbers. "f" + * contains the rounding mode etc. + */ +unsigned long +ieee_ADDT (int f, unsigned long a, unsigned long b, unsigned long *c) +{ + fpclass_t a_type, b_type; + EXTENDED op_a, op_b, op_c; + + a_type = extend_ieee(a, &op_a, DOUBLE); + b_type = extend_ieee(b, &op_b, DOUBLE); + if ((a_type >= NaN && a_type <= INFTY) || + (b_type >= NaN && b_type <= INFTY)) + { + /* propagate NaNs according to arch. ref. handbook: */ + if (b_type == QNaN) + *c = b; + else if (b_type == NaN) + *c = b | (1UL << 51); + else if (a_type == QNaN) + *c = a; + else if (a_type == NaN) + *c = a | (1UL << 51); + + if (a_type == NaN || b_type == NaN) + return FPCR_INV; + if (a_type == QNaN || b_type == QNaN) + return 0; + + if (a_type == INFTY && b_type == INFTY && sign(a) != sign(b)) { + *c = IEEE_QNaN; + return FPCR_INV; + } + if (a_type == INFTY) + *c = a; + else + *c = b; + return 0; + } + add_kernel_ieee(&op_a, &op_b, &op_c); + /* special case for -0 + -0 ==> -0 */ + if (a_type == ZERO && b_type == ZERO) + op_c.s = op_a.s && op_b.s; + + return round_t_ieee(f, &op_c, c); +} + + +/* + * Subtract a - b = c, where a, b, and c are ieee s-floating numbers. + * "f" contains the rounding mode etc. + */ +unsigned long +ieee_SUBS (int f, unsigned long a, unsigned long b, unsigned long *c) +{ + fpclass_t a_type, b_type; + EXTENDED op_a, op_b, op_c; + + a_type = extend_ieee(a, &op_a, SINGLE); + b_type = extend_ieee(b, &op_b, SINGLE); + if ((a_type >= NaN && a_type <= INFTY) || + (b_type >= NaN && b_type <= INFTY)) + { + /* propagate NaNs according to arch. ref. handbook: */ + if (b_type == QNaN) + *c = b; + else if (b_type == NaN) + *c = b | (1UL << 51); + else if (a_type == QNaN) + *c = a; + else if (a_type == NaN) + *c = a | (1UL << 51); + + if (a_type == NaN || b_type == NaN) + return FPCR_INV; + if (a_type == QNaN || b_type == QNaN) + return 0; + + if (a_type == INFTY && b_type == INFTY && sign(a) == sign(b)) { + *c = IEEE_QNaN; + return FPCR_INV; + } + if (a_type == INFTY) + *c = a; + else + *c = b ^ (1UL << 63); + return 0; + } + op_b.s = !op_b.s; + add_kernel_ieee(&op_a, &op_b, &op_c); + /* special case for -0 - +0 ==> -0 */ + if (a_type == ZERO && b_type == ZERO) + op_c.s = op_a.s && op_b.s; + + return round_s_ieee(f, &op_c, c); +} + + +/* + * Subtract a - b = c, where a, b, and c are ieee t-floating numbers. + * "f" contains the rounding mode etc. + */ +unsigned long +ieee_SUBT (int f, unsigned long a, unsigned long b, unsigned long *c) +{ + fpclass_t a_type, b_type; + EXTENDED op_a, op_b, op_c; + + a_type = extend_ieee(a, &op_a, DOUBLE); + b_type = extend_ieee(b, &op_b, DOUBLE); + if ((a_type >= NaN && a_type <= INFTY) || + (b_type >= NaN && b_type <= INFTY)) + { + /* propagate NaNs according to arch. ref. handbook: */ + if (b_type == QNaN) + *c = b; + else if (b_type == NaN) + *c = b | (1UL << 51); + else if (a_type == QNaN) + *c = a; + else if (a_type == NaN) + *c = a | (1UL << 51); + + if (a_type == NaN || b_type == NaN) + return FPCR_INV; + if (a_type == QNaN || b_type == QNaN) + return 0; + + if (a_type == INFTY && b_type == INFTY && sign(a) == sign(b)) { + *c = IEEE_QNaN; + return FPCR_INV; + } + if (a_type == INFTY) + *c = a; + else + *c = b ^ (1UL << 63); + return 0; + } + op_b.s = !op_b.s; + add_kernel_ieee(&op_a, &op_b, &op_c); + /* special case for -0 - +0 ==> -0 */ + if (a_type == ZERO && b_type == ZERO) + op_c.s = op_a.s && op_b.s; + + return round_t_ieee(f, &op_c, c); +} + + +/* + * Multiply a x b = c, where a, b, and c are ieee s-floating numbers. + * "f" contains the rounding mode. + */ +unsigned long +ieee_MULS (int f, unsigned long a, unsigned long b, unsigned long *c) +{ + fpclass_t a_type, b_type; + EXTENDED op_a, op_b, op_c; + + a_type = extend_ieee(a, &op_a, SINGLE); + b_type = extend_ieee(b, &op_b, SINGLE); + if ((a_type >= NaN && a_type <= INFTY) || + (b_type >= NaN && b_type <= INFTY)) + { + /* propagate NaNs according to arch. ref. handbook: */ + if (b_type == QNaN) + *c = b; + else if (b_type == NaN) + *c = b | (1UL << 51); + else if (a_type == QNaN) + *c = a; + else if (a_type == NaN) + *c = a | (1UL << 51); + + if (a_type == NaN || b_type == NaN) + return FPCR_INV; + if (a_type == QNaN || b_type == QNaN) + return 0; + + if ((a_type == INFTY && b_type == ZERO) || + (b_type == INFTY && a_type == ZERO)) + { + *c = IEEE_QNaN; /* return canonical QNaN */ + return FPCR_INV; + } + if (a_type == INFTY) + *c = a ^ ((b >> 63) << 63); + else if (b_type == INFTY) + *c = b ^ ((a >> 63) << 63); + else + /* either of a and b are +/-0 */ + *c = ((unsigned long) op_a.s ^ op_b.s) << 63; + return 0; + } + op_c.s = op_a.s ^ op_b.s; + op_c.e = op_a.e + op_b.e; + mul64(op_a.f[0], op_b.f[0], op_c.f); + + normalize(&op_c); + op_c.e -= 55; /* drop the 55 original bits. */ + + return round_s_ieee(f, &op_c, c); +} + + +/* + * Multiply a x b = c, where a, b, and c are ieee t-floating numbers. + * "f" contains the rounding mode. + */ +unsigned long +ieee_MULT (int f, unsigned long a, unsigned long b, unsigned long *c) +{ + fpclass_t a_type, b_type; + EXTENDED op_a, op_b, op_c; + + *c = IEEE_QNaN; + a_type = extend_ieee(a, &op_a, DOUBLE); + b_type = extend_ieee(b, &op_b, DOUBLE); + if ((a_type >= NaN && a_type <= ZERO) || + (b_type >= NaN && b_type <= ZERO)) + { + /* propagate NaNs according to arch. ref. handbook: */ + if (b_type == QNaN) + *c = b; + else if (b_type == NaN) + *c = b | (1UL << 51); + else if (a_type == QNaN) + *c = a; + else if (a_type == NaN) + *c = a | (1UL << 51); + + if (a_type == NaN || b_type == NaN) + return FPCR_INV; + if (a_type == QNaN || b_type == QNaN) + return 0; + + if ((a_type == INFTY && b_type == ZERO) || + (b_type == INFTY && a_type == ZERO)) + { + *c = IEEE_QNaN; /* return canonical QNaN */ + return FPCR_INV; + } + if (a_type == INFTY) + *c = a ^ ((b >> 63) << 63); + else if (b_type == INFTY) + *c = b ^ ((a >> 63) << 63); + else + /* either of a and b are +/-0 */ + *c = ((unsigned long) op_a.s ^ op_b.s) << 63; + return 0; + } + op_c.s = op_a.s ^ op_b.s; + op_c.e = op_a.e + op_b.e; + mul64(op_a.f[0], op_b.f[0], op_c.f); + + normalize(&op_c); + op_c.e -= 55; /* drop the 55 original bits. */ + + return round_t_ieee(f, &op_c, c); +} + + +/* + * Divide a / b = c, where a, b, and c are ieee s-floating numbers. + * "f" contains the rounding mode etc. + */ +unsigned long +ieee_DIVS (int f, unsigned long a, unsigned long b, unsigned long *c) +{ + fpclass_t a_type, b_type; + EXTENDED op_a, op_b, op_c; + + a_type = extend_ieee(a, &op_a, SINGLE); + b_type = extend_ieee(b, &op_b, SINGLE); + if ((a_type >= NaN && a_type <= ZERO) || + (b_type >= NaN && b_type <= ZERO)) + { + unsigned long res; + + /* propagate NaNs according to arch. ref. handbook: */ + if (b_type == QNaN) + *c = b; + else if (b_type == NaN) + *c = b | (1UL << 51); + else if (a_type == QNaN) + *c = a; + else if (a_type == NaN) + *c = a | (1UL << 51); + + if (a_type == NaN || b_type == NaN) + return FPCR_INV; + if (a_type == QNaN || b_type == QNaN) + return 0; + + res = 0; + *c = IEEE_PINF; + if (a_type == INFTY) { + if (b_type == INFTY) { + *c = IEEE_QNaN; + return FPCR_INV; + } + } else if (b_type == ZERO) { + if (a_type == ZERO) { + *c = IEEE_QNaN; + return FPCR_INV; + } + res = FPCR_DZE; + } else + /* a_type == ZERO || b_type == INFTY */ + *c = 0; + *c |= (unsigned long) (op_a.s ^ op_b.s) << 63; + return res; + } + op_c.s = op_a.s ^ op_b.s; + op_c.e = op_a.e - op_b.e; + + op_a.f[1] = op_a.f[0]; + op_a.f[0] = 0; + div128(op_a.f, op_b.f, op_c.f); + if (a_type != ZERO) + /* force a sticky bit because DIVs never hit exact .5: */ + op_c.f[0] |= STICKY_S; + normalize(&op_c); + op_c.e -= 9; /* remove excess exp from original shift */ + return round_s_ieee(f, &op_c, c); +} + + +/* + * Divide a/b = c, where a, b, and c are ieee t-floating numbers. "f" + * contains the rounding mode etc. + */ +unsigned long +ieee_DIVT (int f, unsigned long a, unsigned long b, unsigned long *c) +{ + fpclass_t a_type, b_type; + EXTENDED op_a, op_b, op_c; + + *c = IEEE_QNaN; + a_type = extend_ieee(a, &op_a, DOUBLE); + b_type = extend_ieee(b, &op_b, DOUBLE); + if ((a_type >= NaN && a_type <= ZERO) || + (b_type >= NaN && b_type <= ZERO)) + { + unsigned long res; + + /* propagate NaNs according to arch. ref. handbook: */ + if (b_type == QNaN) + *c = b; + else if (b_type == NaN) + *c = b | (1UL << 51); + else if (a_type == QNaN) + *c = a; + else if (a_type == NaN) + *c = a | (1UL << 51); + + if (a_type == NaN || b_type == NaN) + return FPCR_INV; + if (a_type == QNaN || b_type == QNaN) + return 0; + + res = 0; + *c = IEEE_PINF; + if (a_type == INFTY) { + if (b_type == INFTY) { + *c = IEEE_QNaN; + return FPCR_INV; + } + } else if (b_type == ZERO) { + if (a_type == ZERO) { + *c = IEEE_QNaN; + return FPCR_INV; + } + res = FPCR_DZE; + } else + /* a_type == ZERO || b_type == INFTY */ + *c = 0; + *c |= (unsigned long) (op_a.s ^ op_b.s) << 63; + return res; + } + op_c.s = op_a.s ^ op_b.s; + op_c.e = op_a.e - op_b.e; + + op_a.f[1] = op_a.f[0]; + op_a.f[0] = 0; + div128(op_a.f, op_b.f, op_c.f); + if (a_type != ZERO) + /* force a sticky bit because DIVs never hit exact .5 */ + op_c.f[0] |= STICKY_T; + normalize(&op_c); + op_c.e -= 9; /* remove excess exp from original shift */ + return round_t_ieee(f, &op_c, c); +} |