/*--- 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_atan() -----------------------------------------------------------+ | | +---------------------------------------------------------------------------*/ void poly_atan(FPU_REG *st0_ptr, u_char st0_tag, FPU_REG *st1_ptr, u_char st1_tag) { u_char transformed, inverted, sign1, sign2; int exponent; long int dummy_exp; Xsig accumulator, Numer, Denom, accumulatore, argSignif, argSq, argSqSq; u_char tag; sign1 = getsign(st0_ptr); sign2 = getsign(st1_ptr); if ( st0_tag == TAG_Valid ) { exponent = exponent(st0_ptr); } else { /* This gives non-compatible stack contents... */ FPU_to_exp16(st0_ptr, st0_ptr); exponent = exponent16(st0_ptr); } if ( st1_tag == TAG_Valid ) { exponent -= exponent(st1_ptr); } else { /* This gives non-compatible stack contents... */ FPU_to_exp16(st1_ptr, st1_ptr); exponent -= exponent16(st1_ptr); } if ( (exponent < 0) || ((exponent == 0) && ((st0_ptr->sigh < st1_ptr->sigh) || ((st0_ptr->sigh == st1_ptr->sigh) && (st0_ptr->sigl < st1_ptr->sigl))) ) ) { inverted = 1; Numer.lsw = Denom.lsw = 0; XSIG_LL(Numer) = significand(st0_ptr); XSIG_LL(Denom) = significand(st1_ptr); } else { inverted = 0; exponent = -exponent; Numer.lsw = Denom.lsw = 0; XSIG_LL(Numer) = significand(st1_ptr); XSIG_LL(Denom) = significand(st0_ptr); } div_Xsig(&Numer, &Denom, &argSignif); exponent += norm_Xsig(&argSignif); if ( (exponent >= -1) || ((exponent == -2) && (argSignif.msw > 0xd413ccd0)) ) { /* The argument is greater than sqrt(2)-1 (=0.414213562...) */ /* Convert the argument by an identity for atan */ transformed = 1; if ( exponent >= 0 ) { #ifdef PARANOID if ( !( (exponent == 0) && (argSignif.lsw == 0) && (argSignif.midw == 0) && (argSignif.msw == 0x80000000) ) ) { EXCEPTION(EX_INTERNAL|0x104); /* There must be a logic error */ return; } #endif /* PARANOID */ argSignif.msw = 0; /* Make the transformed arg -> 0.0 */ } else { Numer.lsw = Denom.lsw = argSignif.lsw; XSIG_LL(Numer) = XSIG_LL(Denom) = XSIG_LL(argSignif); if ( exponent < -1 ) shr_Xsig(&Numer, -1-exponent); negate_Xsig(&Numer); shr_Xsig(&Denom, -exponent); Denom.msw |= 0x80000000; div_Xsig(&Numer, &Denom, &argSignif); exponent = -1 + norm_Xsig(&argSignif); } } else { transformed = 0; } argSq.lsw = argSignif.lsw; argSq.midw = argSignif.midw; argSq.msw = argSignif.msw; mul_Xsig_Xsig(&argSq, &argSq); argSqSq.lsw = argSq.lsw; argSqSq.midw = argSq.midw; argSqSq.msw = argSq.msw; mul_Xsig_Xsig(&argSqSq, &argSqSq); accumulatore.lsw = argSq.lsw; XSIG_LL(accumulatore) = XSIG_LL(argSq); shr_Xsig(&argSq, 2*(-1-exponent-1)); shr_Xsig(&argSqSq, 4*(-1-exponent-1)); /* Now have argSq etc with binary point at the left .1xxxxxxxx */ /* Do the basic fixed point polynomial evaluation */ accumulator.msw = accumulator.midw = accumulator.lsw = 0; polynomial_Xsig(&accumulator, &XSIG_LL(argSqSq), oddplterms, HIPOWERop-1); mul64_Xsig(&accumulator, &XSIG_LL(argSq)); negate_Xsig(&accumulator); polynomial_Xsig(&accumulator, &XSIG_LL(argSqSq), oddnegterms, HIPOWERon-1); negate_Xsig(&accumulator); add_two_Xsig(&accumulator, &fixedpterm, &dummy_exp); mul64_Xsig(&accumulatore, &denomterm); shr_Xsig(&accumulatore, 1 + 2*(-1-exponent)); accumulatore.msw |= 0x80000000; div_Xsig(&accumulator, &accumulatore, &accumulator); mul_Xsig_Xsig(&accumulator, &argSignif); mul_Xsig_Xsig(&accumulator, &argSq); shr_Xsig(&accumulator, 3); negate_Xsig(&accumulator); add_Xsig_Xsig(&accumulator, &argSignif); if ( transformed ) { /* compute pi/4 - accumulator */ shr_Xsig(&accumulator, -1-exponent); negate_Xsig(&accumulator); add_Xsig_Xsig(&accumulator, &pi_signif); exponent = -1; } if ( inverted ) { /* compute pi/2 - accumulator */ shr_Xsig(&accumulator, -exponent); negate_Xsig(&accumulator); add_Xsig_Xsig(&accumulator, &pi_signif); exponent = 0; } if ( sign1 ) { /* compute pi - accumulator */ shr_Xsig(&accumulator, 1 - exponent); negate_Xsig(&accumulator); add_Xsig_Xsig(&accumulator, &pi_signif); exponent = 1; } exponent += round_Xsig(&accumulator); significand(st1_ptr) = XSIG_LL(accumulator); setexponent16(st1_ptr, exponent); tag = FPU_round(st1_ptr, 1, 0, FULL_PRECISION, sign2); FPU_settagi(1, tag); set_precision_flag_up(); /* We do not really know if up or down, use this as the default. */ }
/*--- poly_2xm1() -----------------------------------------------------------+ | Requires st(0) which is TAG_Valid and < 1. | +---------------------------------------------------------------------------*/ int poly_2xm1(u_char sign, FPU_REG *arg, FPU_REG *result) { s32 exponent, shift; u64 Xll; Xsig accumulator, Denom, argSignif; u_char tag; exponent = exponent16(arg); #ifdef PARANOID if ( exponent >= 0 ) /* Don't want a |number| >= 1.0 */ { /* Number negative, too large, or not Valid. */ EXCEPTION(EX_INTERNAL|0x127); return 1; } #endif /* PARANOID */ argSignif.lsw = 0; XSIG_LL(argSignif) = Xll = significand(arg); if ( exponent == -1 ) { shift = (argSignif.msw & 0x40000000) ? 3 : 2; /* subtract 0.5 or 0.75 */ exponent -= 2; XSIG_LL(argSignif) <<= 2; Xll <<= 2; } else if ( exponent == -2 ) { shift = 1; /* subtract 0.25 */ exponent--; XSIG_LL(argSignif) <<= 1; Xll <<= 1; } else shift = 0; if ( exponent < -2 ) { /* Shift the argument right by the required places. */ if ( FPU_shrx(&Xll, -2-exponent) >= 0x80000000U ) Xll++; /* round up */ } accumulator.lsw = accumulator.midw = accumulator.msw = 0; polynomial_Xsig(&accumulator, &Xll, lterms, HIPOWER-1); mul_Xsig_Xsig(&accumulator, &argSignif); shr_Xsig(&accumulator, 3); mul_Xsig_Xsig(&argSignif, &hiterm); /* The leading term */ add_two_Xsig(&accumulator, &argSignif, &exponent); if ( shift ) { /* The argument is large, use the identity: f(x+a) = f(a) * (f(x) + 1) - 1; */ shr_Xsig(&accumulator, - exponent); accumulator.msw |= 0x80000000; /* add 1.0 */ mul_Xsig_Xsig(&accumulator, shiftterm[shift]); accumulator.msw &= 0x3fffffff; /* subtract 1.0 */ exponent = 1; } if ( sign != SIGN_POS ) { /* The argument is negative, use the identity: f(-x) = -f(x) / (1 + f(x)) */ Denom.lsw = accumulator.lsw; XSIG_LL(Denom) = XSIG_LL(accumulator); if ( exponent < 0 ) shr_Xsig(&Denom, - exponent); else if ( exponent > 0 ) { /* exponent must be 1 here */ XSIG_LL(Denom) <<= 1; if ( Denom.lsw & 0x80000000 ) XSIG_LL(Denom) |= 1; (Denom.lsw) <<= 1; } Denom.msw |= 0x80000000; /* add 1.0 */ div_Xsig(&accumulator, &Denom, &accumulator); } /* Convert to 64 bit signed-compatible */ exponent += round_Xsig(&accumulator); result = &st(0); significand(result) = XSIG_LL(accumulator); setexponent16(result, exponent); tag = FPU_round(result, 1, 0, FULL_PRECISION, sign); setsign(result, sign); FPU_settag0(tag); 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); /* 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); }
int FPU_u_mul(const FPU_REG *a, const FPU_REG *b, FPU_REG *c, u16 cw, u_char sign, int expon) { u64 mu, ml, mi; u32 lh, ll, th, tl; #ifdef PARANOID if ( ! (a->sigh & 0x80000000) || ! (b->sigh & 0x80000000) ) { EXCEPTION(EX_INTERNAL|0x205); } #endif ml = a->sigl; ml *= b->sigl; ll = ml; lh = ml >> 32; mu = a->sigh; mu *= b->sigh; mi = a->sigh; mi *= b->sigl; tl = mi; th = mi >> 32; lh += tl; if ( tl > lh ) mu ++; mu += th; mi = a->sigl; mi *= b->sigh; tl = mi; th = mi >> 32; lh += tl; if ( tl > lh ) mu ++; mu += th; ml = lh; ml <<= 32; ml += ll; expon -= EXP_BIAS-1; if ( expon <= EXP_WAY_UNDER ) expon = EXP_WAY_UNDER; c->exp = expon; if ( ! (mu & BX_CONST64(0x8000000000000000)) ) { mu <<= 1; if ( ml & BX_CONST64(0x8000000000000000) ) mu |= 1; ml <<= 1; c->exp --; } ll = ml; lh = ml >> 32; if ( ll ) lh |= 1; c->sigl = mu; c->sigh = mu >> 32; return FPU_round(c, lh, 0, cw, sign); }
void poly_atan(FPU_REG *st0_ptr, u_char st0_tag, FPU_REG *st1_ptr, u_char st1_tag) { u_char transformed, inverted, sign1, sign2; int exponent; long int dummy_exp; Xsig accumulator, Numer, Denom, accumulatore, argSignif, argSq, argSqSq; u_char tag; sign1 = getsign(st0_ptr); sign2 = getsign(st1_ptr); if (st0_tag == TAG_Valid) { exponent = exponent(st0_ptr); } else { FPU_to_exp16(st0_ptr, st0_ptr); exponent = exponent16(st0_ptr); } if (st1_tag == TAG_Valid) { exponent -= exponent(st1_ptr); } else { FPU_to_exp16(st1_ptr, st1_ptr); exponent -= exponent16(st1_ptr); } if ((exponent < 0) || ((exponent == 0) && ((st0_ptr->sigh < st1_ptr->sigh) || ((st0_ptr->sigh == st1_ptr->sigh) && (st0_ptr->sigl < st1_ptr->sigl))))) { inverted = 1; Numer.lsw = Denom.lsw = 0; XSIG_LL(Numer) = significand(st0_ptr); XSIG_LL(Denom) = significand(st1_ptr); } else { inverted = 0; exponent = -exponent; Numer.lsw = Denom.lsw = 0; XSIG_LL(Numer) = significand(st1_ptr); XSIG_LL(Denom) = significand(st0_ptr); } div_Xsig(&Numer, &Denom, &argSignif); exponent += norm_Xsig(&argSignif); if ((exponent >= -1) || ((exponent == -2) && (argSignif.msw > 0xd413ccd0))) { transformed = 1; if (exponent >= 0) { #ifdef PARANOID if (!((exponent == 0) && (argSignif.lsw == 0) && (argSignif.midw == 0) && (argSignif.msw == 0x80000000))) { EXCEPTION(EX_INTERNAL | 0x104); return; } #endif argSignif.msw = 0; } else { Numer.lsw = Denom.lsw = argSignif.lsw; XSIG_LL(Numer) = XSIG_LL(Denom) = XSIG_LL(argSignif); if (exponent < -1) shr_Xsig(&Numer, -1 - exponent); negate_Xsig(&Numer); shr_Xsig(&Denom, -exponent); Denom.msw |= 0x80000000; div_Xsig(&Numer, &Denom, &argSignif); exponent = -1 + norm_Xsig(&argSignif); } } else { transformed = 0; } argSq.lsw = argSignif.lsw; argSq.midw = argSignif.midw; argSq.msw = argSignif.msw; mul_Xsig_Xsig(&argSq, &argSq); argSqSq.lsw = argSq.lsw; argSqSq.midw = argSq.midw; argSqSq.msw = argSq.msw; mul_Xsig_Xsig(&argSqSq, &argSqSq); accumulatore.lsw = argSq.lsw; XSIG_LL(accumulatore) = XSIG_LL(argSq); shr_Xsig(&argSq, 2 * (-1 - exponent - 1)); shr_Xsig(&argSqSq, 4 * (-1 - exponent - 1)); accumulator.msw = accumulator.midw = accumulator.lsw = 0; polynomial_Xsig(&accumulator, &XSIG_LL(argSqSq), oddplterms, HIPOWERop - 1); mul64_Xsig(&accumulator, &XSIG_LL(argSq)); negate_Xsig(&accumulator); polynomial_Xsig(&accumulator, &XSIG_LL(argSqSq), oddnegterms, HIPOWERon - 1); negate_Xsig(&accumulator); add_two_Xsig(&accumulator, &fixedpterm, &dummy_exp); mul64_Xsig(&accumulatore, &denomterm); shr_Xsig(&accumulatore, 1 + 2 * (-1 - exponent)); accumulatore.msw |= 0x80000000; div_Xsig(&accumulator, &accumulatore, &accumulator); mul_Xsig_Xsig(&accumulator, &argSignif); mul_Xsig_Xsig(&accumulator, &argSq); shr_Xsig(&accumulator, 3); negate_Xsig(&accumulator); add_Xsig_Xsig(&accumulator, &argSignif); if (transformed) { shr_Xsig(&accumulator, -1 - exponent); negate_Xsig(&accumulator); add_Xsig_Xsig(&accumulator, &pi_signif); exponent = -1; } if (inverted) { shr_Xsig(&accumulator, -exponent); negate_Xsig(&accumulator); add_Xsig_Xsig(&accumulator, &pi_signif); exponent = 0; } if (sign1) { shr_Xsig(&accumulator, 1 - exponent); negate_Xsig(&accumulator); add_Xsig_Xsig(&accumulator, &pi_signif); exponent = 1; } exponent += round_Xsig(&accumulator); significand(st1_ptr) = XSIG_LL(accumulator); setexponent16(st1_ptr, exponent); tag = FPU_round(st1_ptr, 1, 0, FULL_PRECISION, sign2); FPU_settagi(1, tag); set_precision_flag_up(); }
int poly_2xm1(u_char sign, FPU_REG *arg, FPU_REG *result) { long int exponent, shift; unsigned long long Xll; Xsig accumulator, Denom, argSignif; u_char tag; exponent = exponent16(arg); #ifdef PARANOID if (exponent >= 0) { /* */ /* */ EXCEPTION(EX_INTERNAL | 0x127); return 1; } #endif /* */ argSignif.lsw = 0; XSIG_LL(argSignif) = Xll = significand(arg); if (exponent == -1) { shift = (argSignif.msw & 0x40000000) ? 3 : 2; /* */ exponent -= 2; XSIG_LL(argSignif) <<= 2; Xll <<= 2; } else if (exponent == -2) { shift = 1; /* */ exponent--; XSIG_LL(argSignif) <<= 1; Xll <<= 1; } else shift = 0; if (exponent < -2) { /* */ if (FPU_shrx(&Xll, -2 - exponent) >= 0x80000000U) Xll++; /* */ } accumulator.lsw = accumulator.midw = accumulator.msw = 0; polynomial_Xsig(&accumulator, &Xll, lterms, HIPOWER - 1); mul_Xsig_Xsig(&accumulator, &argSignif); shr_Xsig(&accumulator, 3); mul_Xsig_Xsig(&argSignif, &hiterm); /* */ add_two_Xsig(&accumulator, &argSignif, &exponent); if (shift) { /* */ shr_Xsig(&accumulator, -exponent); accumulator.msw |= 0x80000000; /* */ mul_Xsig_Xsig(&accumulator, shiftterm[shift]); accumulator.msw &= 0x3fffffff; /* */ exponent = 1; } if (sign != SIGN_POS) { /* */ Denom.lsw = accumulator.lsw; XSIG_LL(Denom) = XSIG_LL(accumulator); if (exponent < 0) shr_Xsig(&Denom, -exponent); else if (exponent > 0) { /* */ XSIG_LL(Denom) <<= 1; if (Denom.lsw & 0x80000000) XSIG_LL(Denom) |= 1; (Denom.lsw) <<= 1; } Denom.msw |= 0x80000000; /* */ div_Xsig(&accumulator, &Denom, &accumulator); } /* */ exponent += round_Xsig(&accumulator); result = &st(0); significand(result) = XSIG_LL(accumulator); setexponent16(result, exponent); tag = FPU_round(result, 1, 0, FULL_PRECISION, sign); setsign(result, sign); FPU_settag0(tag); return 0; }
/*--- poly_l2() -------------------------------------------------------------+ | Base 2 logarithm by a polynomial approximation. | +---------------------------------------------------------------------------*/ void poly_l2(FPU_REG *st0_ptr, FPU_REG *st1_ptr, u_char st1_sign) { long int exponent, expon, expon_expon; Xsig accumulator, expon_accum, yaccum; u_char sign, argsign; FPU_REG x; int tag; exponent = exponent16(st0_ptr); /* From st0_ptr, make a number > sqrt(2)/2 and < sqrt(2) */ if (st0_ptr->sigh > (unsigned)0xb504f334) { /* Treat as sqrt(2)/2 < st0_ptr < 1 */ significand(&x) = -significand(st0_ptr); setexponent16(&x, -1); exponent++; argsign = SIGN_NEG; } else { /* Treat as 1 <= st0_ptr < sqrt(2) */ x.sigh = st0_ptr->sigh - 0x80000000; x.sigl = st0_ptr->sigl; setexponent16(&x, 0); argsign = SIGN_POS; } tag = FPU_normalize_nuo(&x); if (tag == TAG_Zero) { expon = 0; accumulator.msw = accumulator.midw = accumulator.lsw = 0; } else { log2_kernel(&x, argsign, &accumulator, &expon); } if (exponent < 0) { sign = SIGN_NEG; exponent = -exponent; } else sign = SIGN_POS; expon_accum.msw = exponent; expon_accum.midw = expon_accum.lsw = 0; if (exponent) { expon_expon = 31 + norm_Xsig(&expon_accum); shr_Xsig(&accumulator, expon_expon - expon); if (sign ^ argsign) negate_Xsig(&accumulator); add_Xsig_Xsig(&accumulator, &expon_accum); } else { expon_expon = expon; sign = argsign; } yaccum.lsw = 0; XSIG_LL(yaccum) = significand(st1_ptr); mul_Xsig_Xsig(&accumulator, &yaccum); expon_expon += round_Xsig(&accumulator); if (accumulator.msw == 0) { FPU_copy_to_reg1(&CONST_Z, TAG_Zero); return; } significand(st1_ptr) = XSIG_LL(accumulator); setexponent16(st1_ptr, expon_expon + exponent16(st1_ptr) + 1); tag = FPU_round(st1_ptr, 1, 0, FULL_PRECISION, sign ^ st1_sign); FPU_settagi(1, tag); set_precision_flag_up(); /* 80486 appears to always do this */ return; }