static void fsqrt_(FPU_REG *st0_ptr, u_char st0_tag) { int expon; clear_C1(); if (st0_tag == TAG_Valid) { u_char tag; if (signnegative(st0_ptr)) { arith_invalid(0); return; } expon = exponent(st0_ptr); denormal_arg: setexponent16(st0_ptr, (expon & 1)); tag = wm_sqrt(st0_ptr, 0, 0, control_word, SIGN_POS); addexponent(st0_ptr, expon >> 1); FPU_settag0(tag); return; }
static void fsqrt_(void) { if (!(FPU_st0_tag ^ TW_Valid)) { int expon; if (FPU_st0_ptr->sign == SIGN_NEG) { arith_invalid(FPU_st0_ptr); /* sqrt(negative) is * invalid */ return; } #ifdef DENORM_OPERAND if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand())) return; #endif /* DENORM_OPERAND */ expon = FPU_st0_ptr->exp - EXP_BIAS; FPU_st0_ptr->exp = EXP_BIAS + (expon & 1); /* make st(0) in [1.0 * .. 4.0) */ wm_sqrt(FPU_st0_ptr, control_word); /* Do the computation */ FPU_st0_ptr->exp += expon >> 1; FPU_st0_ptr->sign = SIGN_POS; } else if (FPU_st0_tag == TW_Zero)
static void fsqrt_(FPU_REG *st0_ptr, u_char st0_tag) { int expon; clear_C1(); if (st0_tag == TAG_Valid) { u_char tag; if (signnegative(st0_ptr)) { arith_invalid(0); /* sqrt(negative) is invalid */ return; } /* make st(0) in [1.0 .. 4.0) */ expon = exponent(st0_ptr); denormal_arg: setexponent16(st0_ptr, (expon & 1)); /* Do the computation, the sign of the result will be positive. */ tag = wm_sqrt(st0_ptr, 0, 0, control_word, SIGN_POS); addexponent(st0_ptr, expon >> 1); FPU_settag0(tag); return; }
/*--- poly_l2p1() -----------------------------------------------------------+ | Base 2 logarithm by a polynomial approximation. | | log2(x+1) | +---------------------------------------------------------------------------*/ int poly_l2p1(u_char sign0, u_char sign1, FPU_REG *st0_ptr, FPU_REG *st1_ptr, FPU_REG *dest) { u_char tag; s32 exponent; Xsig accumulator, yaccum; if ( exponent16(st0_ptr) < 0 ) { log2_kernel(st0_ptr, sign0, &accumulator, &exponent); yaccum.lsw = 0; XSIG_LL(yaccum) = significand(st1_ptr); mul_Xsig_Xsig(&accumulator, &yaccum); exponent += round_Xsig(&accumulator); exponent += exponent16(st1_ptr) + 1; if ( exponent < EXP_WAY_UNDER ) exponent = EXP_WAY_UNDER; significand(dest) = XSIG_LL(accumulator); setexponent16(dest, exponent); tag = FPU_round(dest, 1, 0, FULL_PRECISION, sign0 ^ sign1); FPU_settagi(1, tag); if ( tag == TAG_Valid ) set_precision_flag_up(); /* 80486 appears to always do this */ } else { /* The magnitude of st0_ptr is far too large. */ if ( sign0 != SIGN_POS ) { /* Trying to get the log of a negative number. */ #ifdef PECULIAR_486 /* Stupid 80486 doesn't worry about log(negative). */ changesign(st1_ptr); #else if ( arith_invalid(1) < 0 ) return 1; #endif /* PECULIAR_486 */ } /* 80486 appears to do this */ if ( sign0 == SIGN_NEG ) set_precision_flag_down(); else set_precision_flag_up(); } if ( exponent(dest) <= EXP_UNDER ) EXCEPTION(EX_Underflow); return 0; }
static void fptan(FPU_REG *st0_ptr, u_char st0_tag) { FPU_REG *st_new_ptr; int q; u_char arg_sign = getsign(st0_ptr); if (st0_tag == TAG_Empty) { FPU_stack_underflow(); if (control_word & CW_Invalid) { st_new_ptr = &st(-1); push(); FPU_stack_underflow(); } return; } if (STACK_OVERFLOW) { FPU_stack_overflow(); return; } if (st0_tag == TAG_Valid) { if (exponent(st0_ptr) > -40) { if ((q = trig_arg(st0_ptr, 0)) == -1) { return; } poly_tan(st0_ptr); setsign(st0_ptr, (q & 1) ^ (arg_sign != 0)); set_precision_flag_up(); } else { denormal_arg: FPU_to_exp16(st0_ptr, st0_ptr); st0_tag = FPU_round(st0_ptr, 1, 0, FULL_PRECISION, arg_sign); FPU_settag0(st0_tag); } push(); FPU_copy_to_reg0(&CONST_1, TAG_Valid); return; } if (st0_tag == TAG_Zero) { push(); FPU_copy_to_reg0(&CONST_1, TAG_Valid); setcc(0); return; } if (st0_tag == TAG_Special) st0_tag = FPU_Special(st0_ptr); if (st0_tag == TW_Denormal) { if (denormal_operand() < 0) return; goto denormal_arg; } if (st0_tag == TW_Infinity) { if (arith_invalid(0) >= 0) { st_new_ptr = &st(-1); push(); arith_invalid(0); } return; } single_arg_2_error(st0_ptr, st0_tag); }
/*--- poly_tan() ------------------------------------------------------------+ | | +---------------------------------------------------------------------------*/ void poly_tan(FPU_REG *st0_ptr) { long int exponent; int invert; Xsig argSq, argSqSq, accumulatoro, accumulatore, accum, argSignif, fix_up; unsigned long adj; exponent = exponent(st0_ptr); #ifdef PARANOID if ( signnegative(st0_ptr) ) /* Can't hack a number < 0.0 */ { arith_invalid(0); return; } /* Need a positive number */ #endif PARANOID /* Split the problem into two domains, smaller and larger than pi/4 */ if ( (exponent == 0) || ((exponent == -1) && (st0_ptr->sigh > 0xc90fdaa2)) ) { /* The argument is greater than (approx) pi/4 */ invert = 1; accum.lsw = 0; XSIG_LL(accum) = significand(st0_ptr); if ( exponent == 0 ) { /* The argument is >= 1.0 */ /* Put the binary point at the left. */ XSIG_LL(accum) <<= 1; } /* pi/2 in hex is: 1.921fb54442d18469 898CC51701B839A2 52049C1 */ XSIG_LL(accum) = 0x921fb54442d18469LL - XSIG_LL(accum); /* This is a special case which arises due to rounding. */ if ( XSIG_LL(accum) == 0xffffffffffffffffLL ) { FPU_settag0(TAG_Valid); significand(st0_ptr) = 0x8a51e04daabda360LL; setexponent16(st0_ptr, (0x41 + EXTENDED_Ebias) | SIGN_Negative); return; } argSignif.lsw = accum.lsw; XSIG_LL(argSignif) = XSIG_LL(accum); exponent = -1 + norm_Xsig(&argSignif); } else { invert = 0; argSignif.lsw = 0; XSIG_LL(accum) = XSIG_LL(argSignif) = significand(st0_ptr); if ( exponent < -1 ) { /* shift the argument right by the required places */ if ( FPU_shrx(&XSIG_LL(accum), -1-exponent) >= 0x80000000U ) XSIG_LL(accum) ++; /* round up */ } } XSIG_LL(argSq) = XSIG_LL(accum); argSq.lsw = accum.lsw; mul_Xsig_Xsig(&argSq, &argSq); XSIG_LL(argSqSq) = XSIG_LL(argSq); argSqSq.lsw = argSq.lsw; mul_Xsig_Xsig(&argSqSq, &argSqSq); /* Compute the negative terms for the numerator polynomial */ accumulatoro.msw = accumulatoro.midw = accumulatoro.lsw = 0; polynomial_Xsig(&accumulatoro, &XSIG_LL(argSqSq), oddnegterm, HiPOWERon-1); mul_Xsig_Xsig(&accumulatoro, &argSq); negate_Xsig(&accumulatoro); /* Add the positive terms */ polynomial_Xsig(&accumulatoro, &XSIG_LL(argSqSq), oddplterm, HiPOWERop-1); /* Compute the positive terms for the denominator polynomial */ accumulatore.msw = accumulatore.midw = accumulatore.lsw = 0; polynomial_Xsig(&accumulatore, &XSIG_LL(argSqSq), evenplterm, HiPOWERep-1); mul_Xsig_Xsig(&accumulatore, &argSq); negate_Xsig(&accumulatore); /* Add the negative terms */ polynomial_Xsig(&accumulatore, &XSIG_LL(argSqSq), evennegterm, HiPOWERen-1); /* Multiply by arg^2 */ mul64_Xsig(&accumulatore, &XSIG_LL(argSignif)); mul64_Xsig(&accumulatore, &XSIG_LL(argSignif)); /* de-normalize and divide by 2 */ shr_Xsig(&accumulatore, -2*(1+exponent) + 1); negate_Xsig(&accumulatore); /* This does 1 - accumulator */ /* Now find the ratio. */ if ( accumulatore.msw == 0 ) { /* accumulatoro must contain 1.0 here, (actually, 0) but it really doesn't matter what value we use because it will have negligible effect in later calculations */ XSIG_LL(accum) = 0x8000000000000000LL; accum.lsw = 0; } else { div_Xsig(&accumulatoro, &accumulatore, &accum); } /* Multiply by 1/3 * arg^3 */ mul64_Xsig(&accum, &XSIG_LL(argSignif)); mul64_Xsig(&accum, &XSIG_LL(argSignif)); mul64_Xsig(&accum, &XSIG_LL(argSignif)); mul64_Xsig(&accum, &twothirds); shr_Xsig(&accum, -2*(exponent+1)); /* tan(arg) = arg + accum */ add_two_Xsig(&accum, &argSignif, &exponent); if ( invert ) { /* We now have the value of tan(pi_2 - arg) where pi_2 is an approximation for pi/2 */ /* The next step is to fix the answer to compensate for the error due to the approximation used for pi/2 */ /* This is (approx) delta, the error in our approx for pi/2 (see above). It has an exponent of -65 */ XSIG_LL(fix_up) = 0x898cc51701b839a2LL; fix_up.lsw = 0; if ( exponent == 0 ) adj = 0xffffffff; /* We want approx 1.0 here, but this is close enough. */ else if ( exponent > -30 ) { adj = accum.msw >> -(exponent+1); /* tan */ adj = mul_32_32(adj, adj); /* tan^2 */ } else
static int add_sub_specials(FPU_REG const *a, u_char taga, u_char signa, FPU_REG const *b, u_char tagb, u_char signb, FPU_REG * dest, int deststnr, int control_w) { if (((taga == TW_Denormal) || (tagb == TW_Denormal)) && (denormal_operand() < 0)) return FPU_Exception; if (taga == TAG_Zero) { if (tagb == TAG_Zero) { u_char different_signs = signa ^ signb; FPU_copy_to_regi(a, TAG_Zero, deststnr); if (different_signs) { setsign(dest, ((control_w & CW_RC) != RC_DOWN) ? SIGN_POS : SIGN_NEG); } else setsign(dest, signa); return TAG_Zero; } else { reg_copy(b, dest); if ((tagb == TW_Denormal) && (b->sigh & 0x80000000)) { addexponent(dest, 1); tagb = TAG_Valid; } else if (tagb > TAG_Empty) tagb = TAG_Special; setsign(dest, signb); FPU_settagi(deststnr, tagb); return tagb; } } else if (tagb == TAG_Zero) { reg_copy(a, dest); if ((taga == TW_Denormal) && (a->sigh & 0x80000000)) { addexponent(dest, 1); taga = TAG_Valid; } else if (taga > TAG_Empty) taga = TAG_Special; setsign(dest, signa); FPU_settagi(deststnr, taga); return taga; } else if (taga == TW_Infinity) { if ((tagb != TW_Infinity) || (signa == signb)) { FPU_copy_to_regi(a, TAG_Special, deststnr); setsign(dest, signa); return taga; } return arith_invalid(deststnr); } else if (tagb == TW_Infinity) { FPU_copy_to_regi(b, TAG_Special, deststnr); setsign(dest, signb); return tagb; } #ifdef PARANOID EXCEPTION(EX_INTERNAL | 0x101); #endif return FPU_Exception; }
static int add_sub_specials(FPU_REG const *a, u_char taga, u_char signa, FPU_REG const *b, u_char tagb, u_char signb, FPU_REG *dest, int deststnr, int control_w) { if ( ((taga == TW_Denormal) || (tagb == TW_Denormal)) && (denormal_operand() < 0) ) return FPU_Exception; if (taga == TAG_Zero) { if (tagb == TAG_Zero) { /* Both are zero, result will be zero. */ u_char different_signs = signa ^ signb; FPU_copy_to_regi(a, TAG_Zero, deststnr); if ( different_signs ) { /* Signs are different. */ /* Sign of answer depends upon rounding mode. */ setsign(dest, ((control_w & CW_RC) != RC_DOWN) ? SIGN_POS : SIGN_NEG); } else setsign(dest, signa); /* signa may differ from the sign of a. */ return TAG_Zero; } else { reg_copy(b, dest); if ( (tagb == TW_Denormal) && (b->sigh & 0x80000000) ) { /* A pseudoDenormal, convert it. */ addexponent(dest, 1); tagb = TAG_Valid; } else if ( tagb > TAG_Empty ) tagb = TAG_Special; setsign(dest, signb); /* signb may differ from the sign of b. */ FPU_settagi(deststnr, tagb); return tagb; } } else if (tagb == TAG_Zero) { reg_copy(a, dest); if ( (taga == TW_Denormal) && (a->sigh & 0x80000000) ) { /* A pseudoDenormal */ addexponent(dest, 1); taga = TAG_Valid; } else if ( taga > TAG_Empty ) taga = TAG_Special; setsign(dest, signa); /* signa may differ from the sign of a. */ FPU_settagi(deststnr, taga); return taga; } else if (taga == TW_Infinity) { if ( (tagb != TW_Infinity) || (signa == signb) ) { FPU_copy_to_regi(a, TAG_Special, deststnr); setsign(dest, signa); /* signa may differ from the sign of a. */ return taga; } /* Infinity-Infinity is undefined. */ return arith_invalid(deststnr); } else if (tagb == TW_Infinity) { FPU_copy_to_regi(b, TAG_Special, deststnr); setsign(dest, signb); /* signb may differ from the sign of b. */ return tagb; } #ifdef PARANOID EXCEPTION(EX_INTERNAL|0x101); #endif return FPU_Exception; }
static void fptan(FPU_REG *st0_ptr, u_char st0_tag) { FPU_REG *st_new_ptr; int q; u_char arg_sign = getsign(st0_ptr); /* Stack underflow has higher priority */ if (st0_tag == TAG_Empty) { FPU_stack_underflow(); /* Puts a QNaN in st(0) */ if (control_word & CW_Invalid) { st_new_ptr = &st(-1); push(); FPU_stack_underflow(); /* Puts a QNaN in the new st(0) */ } return; } if (STACK_OVERFLOW) { FPU_stack_overflow(); return; } if (st0_tag == TAG_Valid) { if (exponent(st0_ptr) > -40) { if ((q = trig_arg(st0_ptr, 0)) == -1) { /* Operand is out of range */ return; } poly_tan(st0_ptr); setsign(st0_ptr, (q & 1) ^ (arg_sign != 0)); set_precision_flag_up(); /* We do not really know if up or down */ } else { /* For a small arg, the result == the argument */ /* Underflow may happen */ denormal_arg: FPU_to_exp16(st0_ptr, st0_ptr); st0_tag = FPU_round(st0_ptr, 1, 0, FULL_PRECISION, arg_sign); FPU_settag0(st0_tag); } push(); FPU_copy_to_reg0(&CONST_1, TAG_Valid); return; } if (st0_tag == TAG_Zero) { push(); FPU_copy_to_reg0(&CONST_1, TAG_Valid); setcc(0); return; } if (st0_tag == TAG_Special) st0_tag = FPU_Special(st0_ptr); if (st0_tag == TW_Denormal) { if (denormal_operand() < 0) return; goto denormal_arg; } if (st0_tag == TW_Infinity) { /* The 80486 treats infinity as an invalid operand */ if (arith_invalid(0) >= 0) { st_new_ptr = &st(-1); push(); arith_invalid(0); } return; } single_arg_2_error(st0_ptr, st0_tag); }
/*--- poly_atan() -----------------------------------------------------------+ | | +---------------------------------------------------------------------------*/ void poly_atan(FPU_REG * arg) { char recursions = 0; short exponent; FPU_REG odd_poly, even_poly, pos_poly, neg_poly; FPU_REG argSq; long long arg_signif, argSqSq; #ifdef PARANOID if (arg->sign != 0) { /* Can't hack a number < 0.0 */ arith_invalid(arg); return; } /* Need a positive number */ #endif /* PARANOID */ exponent = arg->exp - EXP_BIAS; if (arg->tag == TW_Zero) { /* Return 0.0 */ reg_move(&CONST_Z, arg); return; } if (exponent >= -2) { /* argument is in the range [0.25 .. 1.0] */ if (exponent >= 0) { #ifdef PARANOID if ((exponent == 0) && (arg->sigl == 0) && (arg->sigh == 0x80000000)) #endif /* PARANOID */ { reg_move(&CONST_PI4, arg); return; } #ifdef PARANOID EXCEPTION(EX_INTERNAL | 0x104); /* There must be a logic * error */ #endif /* PARANOID */ } /* If the argument is greater than sqrt(2)-1 (=0.414213562...) */ /* convert the argument by an identity for atan */ if ((exponent >= -1) || (arg->sigh > 0xd413ccd0)) { FPU_REG numerator, denom; recursions++; arg_signif = *(long long *) &(arg->sigl); if (exponent < -1) { if (shrx(&arg_signif, -1 - exponent) >= (unsigned)0x80000000) arg_signif++; /* round up */ } *(long long *) &(numerator.sigl) = -arg_signif; numerator.exp = EXP_BIAS - 1; normalize(&numerator); /* 1 - arg */ arg_signif = *(long long *) &(arg->sigl); if (shrx(&arg_signif, -exponent) >= (unsigned)0x80000000) arg_signif++; /* round up */ *(long long *) &(denom.sigl) = arg_signif; denom.sigh |= 0x80000000; /* 1 + arg */ arg->exp = numerator.exp; reg_u_div(&numerator, &denom, arg, FULL_PRECISION); exponent = arg->exp - EXP_BIAS; } } *(long long *) &arg_signif = *(long long *) &(arg->sigl); #ifdef PARANOID /* This must always be true */ if (exponent >= -1) { EXCEPTION(EX_INTERNAL | 0x120); /* There must be a logic error */ } #endif /* PARANOID */ /* shift the argument right by the required places */ if (shrx(&arg_signif, -1 - exponent) >= (unsigned)0x80000000) arg_signif++; /* round up */ /* Now have arg_signif with binary point at the left .1xxxxxxxx */ mul64(&arg_signif, &arg_signif, (long long *) (&argSq.sigl)); mul64((long long *) (&argSq.sigl), (long long *) (&argSq.sigl), &argSqSq); /* will be a valid positive nr with expon = 0 */ *(short *) &(pos_poly.sign) = 0; pos_poly.exp = EXP_BIAS; /* Do the basic fixed point polynomial evaluation */ polynomial(&pos_poly.sigl, (unsigned *) &argSqSq, (unsigned short (*)[4]) oddplterms, HIPOWERop - 1); mul64((long long *) (&argSq.sigl), (long long *) (&pos_poly.sigl), (long long *) (&pos_poly.sigl)); /* will be a valid positive nr with expon = 0 */ *(short *) &(neg_poly.sign) = 0; neg_poly.exp = EXP_BIAS; /* Do the basic fixed point polynomial evaluation */ polynomial(&neg_poly.sigl, (unsigned *) &argSqSq, (unsigned short (*)[4]) oddnegterms, HIPOWERon - 1); /* Subtract the mantissas */ *((long long *) (&pos_poly.sigl)) -= *((long long *) (&neg_poly.sigl)); reg_move(&pos_poly, &odd_poly); poly_add_1(&odd_poly); /* The complete odd polynomial */ reg_u_mul(&odd_poly, arg, &odd_poly, FULL_PRECISION); /* will be a valid positive nr with expon = 0 */ *(short *) &(even_poly.sign) = 0; mul64((long long *) (&argSq.sigl), (long long *) (&denomterm), (long long *) (&even_poly.sigl)); poly_add_1(&even_poly); reg_div(&odd_poly, &even_poly, arg, FULL_PRECISION); if (recursions) reg_sub(&CONST_PI4, arg, arg, FULL_PRECISION); }
/* Divide one register by another and put the result into a third register. */ int FPU_div(int flags, int rm, int control_w) { FPU_REG x, y; FPU_REG const *a, *b, *st0_ptr, *st_ptr; FPU_REG *dest; u_char taga, tagb, signa, signb, sign, saved_sign; int tag, deststnr; if (flags & DEST_RM) deststnr = rm; else deststnr = 0; if (flags & REV) { b = &st(0); st0_ptr = b; tagb = FPU_gettag0(); if (flags & LOADED) { a = (FPU_REG *) rm; taga = flags & 0x0f; } else { a = &st(rm); st_ptr = a; taga = FPU_gettagi(rm); } } else { a = &st(0); st0_ptr = a; taga = FPU_gettag0(); if (flags & LOADED) { b = (FPU_REG *) rm; tagb = flags & 0x0f; } else { b = &st(rm); st_ptr = b; tagb = FPU_gettagi(rm); } } signa = getsign(a); signb = getsign(b); sign = signa ^ signb; dest = &st(deststnr); saved_sign = getsign(dest); if (!(taga | tagb)) { /* Both regs Valid, this should be the most common case. */ reg_copy(a, &x); reg_copy(b, &y); setpositive(&x); setpositive(&y); tag = FPU_u_div(&x, &y, dest, control_w, sign); if (tag < 0) return tag; FPU_settagi(deststnr, tag); return tag; } if (taga == TAG_Special) taga = FPU_Special(a); if (tagb == TAG_Special) tagb = FPU_Special(b); if (((taga == TAG_Valid) && (tagb == TW_Denormal)) || ((taga == TW_Denormal) && (tagb == TAG_Valid)) || ((taga == TW_Denormal) && (tagb == TW_Denormal))) { if (denormal_operand() < 0) return FPU_Exception; FPU_to_exp16(a, &x); FPU_to_exp16(b, &y); tag = FPU_u_div(&x, &y, dest, control_w, sign); if (tag < 0) return tag; FPU_settagi(deststnr, tag); return tag; } else if ((taga <= TW_Denormal) && (tagb <= TW_Denormal)) { if (tagb != TAG_Zero) { /* Want to find Zero/Valid */ if (tagb == TW_Denormal) { if (denormal_operand() < 0) return FPU_Exception; } /* The result is zero. */ FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr); setsign(dest, sign); return TAG_Zero; } /* We have an exception condition, either 0/0 or Valid/Zero. */ if (taga == TAG_Zero) { /* 0/0 */ return arith_invalid(deststnr); } /* Valid/Zero */ return FPU_divide_by_zero(deststnr, sign); } /* Must have infinities, NaNs, etc */ else if ((taga == TW_NaN) || (tagb == TW_NaN)) { if (flags & LOADED) return real_2op_NaN((FPU_REG *) rm, flags & 0x0f, 0, st0_ptr); if (flags & DEST_RM) { int tag; tag = FPU_gettag0(); if (tag == TAG_Special) tag = FPU_Special(st0_ptr); return real_2op_NaN(st0_ptr, tag, rm, (flags & REV) ? st0_ptr : &st(rm)); } else { int tag; tag = FPU_gettagi(rm); if (tag == TAG_Special) tag = FPU_Special(&st(rm)); return real_2op_NaN(&st(rm), tag, 0, (flags & REV) ? st0_ptr : &st(rm)); } } else if (taga == TW_Infinity) { if (tagb == TW_Infinity) { /* infinity/infinity */ return arith_invalid(deststnr); } else { /* tagb must be Valid or Zero */ if ((tagb == TW_Denormal) && (denormal_operand() < 0)) return FPU_Exception; /* Infinity divided by Zero or Valid does not raise and exception, but returns Infinity */ FPU_copy_to_regi(a, TAG_Special, deststnr); setsign(dest, sign); return taga; } } else if (tagb == TW_Infinity) { if ((taga == TW_Denormal) && (denormal_operand() < 0)) return FPU_Exception; /* The result is zero. */ FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr); setsign(dest, sign); return TAG_Zero; } #ifdef PARANOID else { EXCEPTION(EX_INTERNAL | 0x102); return FPU_Exception; } #endif /* PARANOID */ return 0; }
/*--- poly_tan() ------------------------------------------------------------+ | | +---------------------------------------------------------------------------*/ void poly_tan(FPU_REG * arg, FPU_REG * y_reg) { char invert = 0; short exponent; FPU_REG odd_poly, even_poly, pos_poly, neg_poly; FPU_REG argSq; long long arg_signif, argSqSq; exponent = arg->exp - EXP_BIAS; if (arg->tag == TW_Zero) { /* Return 0.0 */ reg_move(&CONST_Z, y_reg); return; } if (exponent >= -1) { /* argument is in the range [0.5 .. 1.0] */ if (exponent >= 0) { #ifdef PARANOID if ((exponent == 0) && (arg->sigl == 0) && (arg->sigh == 0x80000000)) #endif /* PARANOID */ { arith_overflow(y_reg); return; } #ifdef PARANOID EXCEPTION(EX_INTERNAL | 0x104); /* There must be a logic * error */ return; #endif /* PARANOID */ } /* The argument is in the range [0.5 .. 1.0) */ /* Convert the argument to a number in the range (0.0 .. 0.5] */ *((long long *) (&arg->sigl)) = -*((long long *) (&arg->sigl)); normalize(arg); /* Needed later */ exponent = arg->exp - EXP_BIAS; invert = 1; } #ifdef PARANOID if (arg->sign != 0) { /* Can't hack a number < 0.0 */ arith_invalid(y_reg); return; } /* Need a positive number */ #endif /* PARANOID */ *(long long *) &arg_signif = *(long long *) &(arg->sigl); if (exponent < -1) { /* shift the argument right by the required places */ if (shrx(&arg_signif, -1 - exponent) >= (unsigned)0x80000000) arg_signif++; /* round up */ } mul64(&arg_signif, &arg_signif, (long long *) (&argSq.sigl)); mul64((long long *) (&argSq.sigl), (long long *) (&argSq.sigl), &argSqSq); /* will be a valid positive nr with expon = 0 */ *(short *) &(pos_poly.sign) = 0; pos_poly.exp = EXP_BIAS; /* Do the basic fixed point polynomial evaluation */ polynomial((u_int *) &pos_poly.sigl, (unsigned *) &argSqSq, oddplterms, HIPOWERop - 1); /* will be a valid positive nr with expon = 0 */ *(short *) &(neg_poly.sign) = 0; neg_poly.exp = EXP_BIAS; /* Do the basic fixed point polynomial evaluation */ polynomial((u_int *) &neg_poly.sigl, (unsigned *) &argSqSq, oddnegterms, HIPOWERon - 1); mul64((long long *) (&argSq.sigl), (long long *) (&neg_poly.sigl), (long long *) (&neg_poly.sigl)); /* Subtract the mantissas */ *((long long *) (&pos_poly.sigl)) -= *((long long *) (&neg_poly.sigl)); /* Convert to 64 bit signed-compatible */ pos_poly.exp -= 1; reg_move(&pos_poly, &odd_poly); normalize(&odd_poly); reg_mul(&odd_poly, arg, &odd_poly, FULL_PRECISION); reg_u_add(&odd_poly, arg, &odd_poly, FULL_PRECISION); /* This is just the odd * polynomial */ /* will be a valid positive nr with expon = 0 */ *(short *) &(pos_poly.sign) = 0; pos_poly.exp = EXP_BIAS; /* Do the basic fixed point polynomial evaluation */ polynomial((u_int *) &pos_poly.sigl, (unsigned *) &argSqSq, evenplterms, HIPOWERep - 1); mul64((long long *) (&argSq.sigl), (long long *) (&pos_poly.sigl), (long long *) (&pos_poly.sigl)); /* will be a valid positive nr with expon = 0 */ *(short *) &(neg_poly.sign) = 0; neg_poly.exp = EXP_BIAS; /* Do the basic fixed point polynomial evaluation */ polynomial((u_int *) &neg_poly.sigl, (unsigned *) &argSqSq, evennegterms, HIPOWERen - 1); /* Subtract the mantissas */ *((long long *) (&neg_poly.sigl)) -= *((long long *) (&pos_poly.sigl)); /* and multiply by argSq */ /* Convert argSq to a valid reg number */ *(short *) &(argSq.sign) = 0; argSq.exp = EXP_BIAS - 1; normalize(&argSq); /* Convert to 64 bit signed-compatible */ neg_poly.exp -= 1; reg_move(&neg_poly, &even_poly); normalize(&even_poly); reg_mul(&even_poly, &argSq, &even_poly, FULL_PRECISION); reg_add(&even_poly, &argSq, &even_poly, FULL_PRECISION); reg_sub(&CONST_1, &even_poly, &even_poly, FULL_PRECISION); /* This is just the even * polynomial */ /* Now ready to copy the results */ if (invert) { reg_div(&even_poly, &odd_poly, y_reg, FULL_PRECISION); } else { reg_div(&odd_poly, &even_poly, y_reg, FULL_PRECISION); } }
int FPU_mul(FPU_REG const *b, u_char tagb, int deststnr, int control_w) { FPU_REG *a = &st(deststnr); FPU_REG *dest = a; u_char taga = FPU_gettagi(deststnr); u_char saved_sign = getsign(dest); u_char sign = (getsign(a) ^ getsign(b)); int tag; if (!(taga | tagb)) { tag = FPU_u_mul(a, b, dest, control_w, sign, exponent(a) + exponent(b)); if (tag < 0) { setsign(dest, saved_sign); return tag; } FPU_settagi(deststnr, tag); return tag; } if (taga == TAG_Special) taga = FPU_Special(a); if (tagb == TAG_Special) tagb = FPU_Special(b); if (((taga == TAG_Valid) && (tagb == TW_Denormal)) || ((taga == TW_Denormal) && (tagb == TAG_Valid)) || ((taga == TW_Denormal) && (tagb == TW_Denormal))) { FPU_REG x, y; if (denormal_operand() < 0) return FPU_Exception; FPU_to_exp16(a, &x); FPU_to_exp16(b, &y); tag = FPU_u_mul(&x, &y, dest, control_w, sign, exponent16(&x) + exponent16(&y)); if (tag < 0) { setsign(dest, saved_sign); return tag; } FPU_settagi(deststnr, tag); return tag; } else if ((taga <= TW_Denormal) && (tagb <= TW_Denormal)) { if (((tagb == TW_Denormal) || (taga == TW_Denormal)) && (denormal_operand() < 0)) return FPU_Exception; FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr); setsign(dest, sign); return TAG_Zero; } else if ((taga == TW_NaN) || (tagb == TW_NaN)) { return real_2op_NaN(b, tagb, deststnr, &st(0)); } else if (((taga == TW_Infinity) && (tagb == TAG_Zero)) || ((tagb == TW_Infinity) && (taga == TAG_Zero))) { return arith_invalid(deststnr); } else if (((taga == TW_Denormal) || (tagb == TW_Denormal)) && (denormal_operand() < 0)) { return FPU_Exception; } else if (taga == TW_Infinity) { FPU_copy_to_regi(a, TAG_Special, deststnr); setsign(dest, sign); return TAG_Special; } else if (tagb == TW_Infinity) { FPU_copy_to_regi(b, TAG_Special, deststnr); setsign(dest, sign); return TAG_Special; } #ifdef PARANOID else { EXCEPTION(EX_INTERNAL | 0x102); return FPU_Exception; } #endif return 0; }
void reg_add(FPU_REG * a, FPU_REG * b, FPU_REG * dest, int control_w) { int diff; if (!(a->tag | b->tag)) { /* Both registers are valid */ if (!(a->sign ^ b->sign)) { /* signs are the same */ reg_u_add(a, b, dest, control_w); dest->sign = a->sign; return; } /* The signs are different, so do a subtraction */ diff = a->exp - b->exp; if (!diff) { diff = a->sigh - b->sigh; /* Works only if ms bits * are identical */ if (!diff) { diff = a->sigl > b->sigl; if (!diff) diff = -(a->sigl < b->sigl); } } if (diff > 0) { reg_u_sub(a, b, dest, control_w); dest->sign = a->sign; } else if (diff == 0) { reg_move(&CONST_Z, dest); /* sign depends upon rounding mode */ dest->sign = ((control_w & CW_RC) != RC_DOWN) ? SIGN_POS : SIGN_NEG; } else { reg_u_sub(b, a, dest, control_w); dest->sign = b->sign; } return; } else { if ((a->tag == TW_NaN) || (b->tag == TW_NaN)) { real_2op_NaN(a, b, dest); return; } else if (a->tag == TW_Zero) { if (b->tag == TW_Zero) { char different_signs = a->sign ^ b->sign; /* Both are zero, result will be zero. */ reg_move(a, dest); if (different_signs) { /* Signs are different. */ /* Sign of answer depends upon * rounding mode. */ dest->sign = ((control_w & CW_RC) != RC_DOWN) ? SIGN_POS : SIGN_NEG; } } else { #ifdef DENORM_OPERAND if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && denormal_operand()) return; #endif /* DENORM_OPERAND */ reg_move(b, dest); } return; } else if (b->tag == TW_Zero) { #ifdef DENORM_OPERAND if ((a->tag == TW_Valid) && (a->exp <= EXP_UNDER) && denormal_operand()) return; #endif /* DENORM_OPERAND */ reg_move(a, dest); return; } else if (a->tag == TW_Infinity) { if (b->tag != TW_Infinity) { #ifdef DENORM_OPERAND if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && denormal_operand()) return; #endif /* DENORM_OPERAND */ reg_move(a, dest); return; } if (a->sign == b->sign) { /* They are both + or * - infinity */ reg_move(a, dest); return; } arith_invalid(dest); /* Infinity-Infinity is * undefined. */ return; } else if (b->tag == TW_Infinity) { #ifdef DENORM_OPERAND if ((a->tag == TW_Valid) && (a->exp <= EXP_UNDER) && denormal_operand()) return; #endif /* DENORM_OPERAND */ reg_move(b, dest); return; } } #ifdef PARANOID EXCEPTION(EX_INTERNAL | 0x101); #endif }
/* Subtract b from a. (a-b) -> dest */ void reg_sub(FPU_REG * a, FPU_REG * b, FPU_REG * dest, int control_w) { int diff; if (!(a->tag | b->tag)) { /* Both registers are valid */ diff = a->exp - b->exp; if (!diff) { diff = a->sigh - b->sigh; /* Works only if ms bits * are identical */ if (!diff) { diff = a->sigl > b->sigl; if (!diff) diff = -(a->sigl < b->sigl); } } switch (a->sign * 2 + b->sign) { case 0: /* P - P */ case 3: /* N - N */ if (diff > 0) { reg_u_sub(a, b, dest, control_w); dest->sign = a->sign; } else if (diff == 0) { #ifdef DENORM_OPERAND if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && denormal_operand()) return; #endif /* DENORM_OPERAND */ reg_move(&CONST_Z, dest); /* sign depends upon rounding mode */ dest->sign = ((control_w & CW_RC) != RC_DOWN) ? SIGN_POS : SIGN_NEG; } else { reg_u_sub(b, a, dest, control_w); dest->sign = a->sign ^ SIGN_POS ^ SIGN_NEG; } return; case 1: /* P - N */ reg_u_add(a, b, dest, control_w); dest->sign = SIGN_POS; return; case 2: /* N - P */ reg_u_add(a, b, dest, control_w); dest->sign = SIGN_NEG; return; } } else { if ((a->tag == TW_NaN) || (b->tag == TW_NaN)) { real_2op_NaN(a, b, dest); return; } else if (b->tag == TW_Zero) { if (a->tag == TW_Zero) { char same_signs = !(a->sign ^ b->sign); /* Both are zero, result will be zero. */ reg_move(a, dest); /* Answer for different * signs. */ if (same_signs) { /* Sign depends upon rounding * mode */ dest->sign = ((control_w & CW_RC) != RC_DOWN) ? SIGN_POS : SIGN_NEG; } } else { #ifdef DENORM_OPERAND if ((a->tag == TW_Valid) && (a->exp <= EXP_UNDER) && denormal_operand()) return; #endif /* DENORM_OPERAND */ reg_move(a, dest); } return; } else if (a->tag == TW_Zero) { #ifdef DENORM_OPERAND if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && denormal_operand()) return; #endif /* DENORM_OPERAND */ reg_move(b, dest); dest->sign ^= SIGN_POS ^ SIGN_NEG; return; } else if (a->tag == TW_Infinity) { if (b->tag != TW_Infinity) { #ifdef DENORM_OPERAND if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && denormal_operand()) return; #endif /* DENORM_OPERAND */ reg_move(a, dest); return; } /* Both args are Infinity */ if (a->sign == b->sign) { arith_invalid(dest); /* Infinity-Infinity is * undefined. */ return; } reg_move(a, dest); return; } else if (b->tag == TW_Infinity) { #ifdef DENORM_OPERAND if ((a->tag == TW_Valid) && (a->exp <= EXP_UNDER) && denormal_operand()) return; #endif /* DENORM_OPERAND */ reg_move(b, dest); dest->sign ^= SIGN_POS ^ SIGN_NEG; return; } } #ifdef PARANOID EXCEPTION(EX_INTERNAL | 0x110); #endif }
int FPU_div(int flags, int rm, int control_w) { FPU_REG x, y; FPU_REG const *a, *b, *st0_ptr, *st_ptr; FPU_REG *dest; u_char taga, tagb, signa, signb, sign, saved_sign; int tag, deststnr; if (flags & DEST_RM) deststnr = rm; else deststnr = 0; if (flags & REV) { b = &st(0); st0_ptr = b; tagb = FPU_gettag0(); if (flags & LOADED) { a = (FPU_REG *) rm; taga = flags & 0x0f; } else { a = &st(rm); st_ptr = a; taga = FPU_gettagi(rm); } } else { a = &st(0); st0_ptr = a; taga = FPU_gettag0(); if (flags & LOADED) { b = (FPU_REG *) rm; tagb = flags & 0x0f; } else { b = &st(rm); st_ptr = b; tagb = FPU_gettagi(rm); } } signa = getsign(a); signb = getsign(b); sign = signa ^ signb; dest = &st(deststnr); saved_sign = getsign(dest); if (!(taga | tagb)) { reg_copy(a, &x); reg_copy(b, &y); setpositive(&x); setpositive(&y); tag = FPU_u_div(&x, &y, dest, control_w, sign); if (tag < 0) return tag; FPU_settagi(deststnr, tag); return tag; } if (taga == TAG_Special) taga = FPU_Special(a); if (tagb == TAG_Special) tagb = FPU_Special(b); if (((taga == TAG_Valid) && (tagb == TW_Denormal)) || ((taga == TW_Denormal) && (tagb == TAG_Valid)) || ((taga == TW_Denormal) && (tagb == TW_Denormal))) { if (denormal_operand() < 0) return FPU_Exception; FPU_to_exp16(a, &x); FPU_to_exp16(b, &y); tag = FPU_u_div(&x, &y, dest, control_w, sign); if (tag < 0) return tag; FPU_settagi(deststnr, tag); return tag; } else if ((taga <= TW_Denormal) && (tagb <= TW_Denormal)) { if (tagb != TAG_Zero) { if (tagb == TW_Denormal) { if (denormal_operand() < 0) return FPU_Exception; } FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr); setsign(dest, sign); return TAG_Zero; } if (taga == TAG_Zero) { return arith_invalid(deststnr); } return FPU_divide_by_zero(deststnr, sign); } else if ((taga == TW_NaN) || (tagb == TW_NaN)) { if (flags & LOADED) return real_2op_NaN((FPU_REG *) rm, flags & 0x0f, 0, st0_ptr); if (flags & DEST_RM) { int tag; tag = FPU_gettag0(); if (tag == TAG_Special) tag = FPU_Special(st0_ptr); return real_2op_NaN(st0_ptr, tag, rm, (flags & REV) ? st0_ptr : &st(rm)); } else { int tag; tag = FPU_gettagi(rm); if (tag == TAG_Special) tag = FPU_Special(&st(rm)); return real_2op_NaN(&st(rm), tag, 0, (flags & REV) ? st0_ptr : &st(rm)); } } else if (taga == TW_Infinity) { if (tagb == TW_Infinity) { return arith_invalid(deststnr); } else { if ((tagb == TW_Denormal) && (denormal_operand() < 0)) return FPU_Exception; FPU_copy_to_regi(a, TAG_Special, deststnr); setsign(dest, sign); return taga; } } else if (tagb == TW_Infinity) { if ((taga == TW_Denormal) && (denormal_operand() < 0)) return FPU_Exception; FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr); setsign(dest, sign); return TAG_Zero; } #ifdef PARANOID else { EXCEPTION(EX_INTERNAL | 0x102); return FPU_Exception; } #endif return 0; }