/* Convert a s32 to register */ static void convert_l2reg(s32 const *arg, int deststnr) { int tag; s32 num = *arg; u_char sign; FPU_REG *dest = &st(deststnr); if (num == 0) { FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr); return; } if (num > 0) { sign = SIGN_POS; } else { num = -num; sign = SIGN_NEG; } dest->sigh = num; dest->sigl = 0; setexponent16(dest, 31); tag = FPU_normalize_nuo(dest, EXTENDED_Ebias); /* No underflow or overflow is possible */ FPU_settagi(deststnr, tag); setsign(dest, sign); return; }
int FPU_to_exp16(FPU_REG const *a, FPU_REG *x) { int sign = getsign(a); *(long long *)&(x->sigl) = *(const long long *)&(a->sigl); /* Set up the exponent as a 16 bit quantity. */ setexponent16(x, exponent(a)); if (exponent16(x) == EXP_UNDER) { /* The number is a de-normal or pseudodenormal. */ /* We only deal with the significand and exponent. */ if (x->sigh & 0x80000000) { /* Is a pseudodenormal. */ /* This is non-80486 behaviour because the number loses its 'denormal' identity. */ addexponent(x, 1); } else { /* Is a denormal. */ addexponent(x, 1); FPU_normalize_nuo(x); } } if (!(x->sigh & 0x80000000)) { EXCEPTION(EX_INTERNAL | 0x180); } return sign; }
int FPU_to_exp16(FPU_REG const *a, FPU_REG *x) { int sign = getsign(a); *(long long *)&(x->sigl) = *(const long long *)&(a->sigl); setexponent16(x, exponent(a)); if (exponent16(x) == EXP_UNDER) { if (x->sigh & 0x80000000) { addexponent(x, 1); } else { addexponent(x, 1); FPU_normalize_nuo(x); } } if (!(x->sigh & 0x80000000)) { EXCEPTION(EX_INTERNAL | 0x180); } return sign; }
/* Limited measurements show no results worse than 64 bit precision except for the results for arguments close to 2^63, where the precision of the result sometimes degrades to about 63.9 bits */ static int trig_arg(FPU_REG *st0_ptr, int flags) { FPU_REG tmp; u_char tmptag; u64 q; int old_cw = control_word, saved_status = partial_status; int tag, st0_tag = TAG_Valid; if ( exponent(st0_ptr) >= 63 ) { partial_status |= SW_C2; /* Reduction incomplete. */ return -1; } if ( flags & FPTAN ) st0_ptr->exp ++; /* Effectively base the following upon pi/4 */ control_word &= ~CW_RC; control_word |= RC_CHOP; setpositive(st0_ptr); tag = FPU_u_div(st0_ptr, &CONST_PI2, &tmp, PR_64_BITS | RC_CHOP | 0x3f, SIGN_POS); FPU_round_to_int(&tmp, tag); /* Fortunately, this can't overflow to 2^64 */ q = significand(&tmp); if ( q ) { rem_kernel(significand(st0_ptr), &significand(&tmp), significand(&CONST_PI2), q, exponent(st0_ptr) - exponent(&CONST_PI2)); setexponent16(&tmp, exponent(&CONST_PI2)); st0_tag = FPU_normalize_nuo(&tmp, EXTENDED_Ebias); /* No underflow or overflow is possible */ FPU_copy_to_reg0(&tmp, st0_tag); } if ( ((flags & FCOS) && !(q & 1)) || (!(flags & FCOS) && (q & 1)) ) { st0_tag = FPU_sub(REV|LOADED|TAG_Valid, &CONST_PI2, FULL_PRECISION); //bbd: arg2 used to typecast to (int) #ifdef BETTER_THAN_486 /* So far, the results are exact but based upon a 64 bit precision approximation to pi/2. The technique used now is equivalent to using an approximation to pi/2 which is accurate to about 128 bits. */ if ( (exponent(st0_ptr) <= exponent(&CONST_PI2extra) + 64) || (q > 1) ) { /* This code gives the effect of having pi/2 to better than 128 bits precision. */ significand(&tmp) = q + 1; setexponent16(&tmp, 63); FPU_normalize_nuo(&tmp, EXTENDED_Ebias); /* No underflow or overflow is possible */ tmptag = FPU_u_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION, SIGN_POS, exponent(&CONST_PI2extra) + exponent(&tmp)); setsign(&tmp, getsign(&CONST_PI2extra)); st0_tag = FPU_add(&tmp, tmptag, 0, FULL_PRECISION); if ( signnegative(st0_ptr) && !(flags & FPTAN) ) { /* CONST_PI2extra is negative, so the result of the addition can be negative. This means that the argument is actually in a different quadrant. The correction is always < pi/2, so it can't overflow into yet another quadrant. */ /* The function is even, so we need just adjust the sign and q. */ setpositive(st0_ptr); q++; } } #endif /* BETTER_THAN_486 */ } #ifdef BETTER_THAN_486 else { /* So far, the results are exact but based upon a 64 bit precision approximation to pi/2. The technique used now is equivalent to using an approximation to pi/2 which is accurate to about 128 bits. */ if ( ((q > 0) && (exponent(st0_ptr) <= exponent(&CONST_PI2extra) + 64)) || (q > 1) ) { /* This code gives the effect of having p/2 to better than 128 bits precision. */ significand(&tmp) = q; setexponent16(&tmp, 63); FPU_normalize_nuo(&tmp, EXTENDED_Ebias); /* No underflow or overflow is possible. This must return TAG_Valid */ tmptag = FPU_u_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION, SIGN_POS, exponent(&CONST_PI2extra) + exponent(&tmp)); setsign(&tmp, getsign(&CONST_PI2extra)); st0_tag = FPU_sub(LOADED|(tmptag & 0x0f), &tmp, FULL_PRECISION); if ( (exponent(st0_ptr) == exponent(&CONST_PI2)) && ((st0_ptr->sigh > CONST_PI2.sigh) || ((st0_ptr->sigh == CONST_PI2.sigh) && (st0_ptr->sigl > CONST_PI2.sigl))) ) { /* CONST_PI2extra is negative, so the result of the subtraction can be larger than pi/2. This means that the argument is actually in a different quadrant. The correction is always < pi/2, so it can't overflow into yet another quadrant. bbd: arg2 used to typecast to (int), corrupting 64-bit ptrs */ st0_tag = FPU_sub(REV|LOADED|TAG_Valid, &CONST_PI2, FULL_PRECISION); q++; } } } #endif /* BETTER_THAN_486 */ FPU_settag0(st0_tag); control_word = old_cw; partial_status = saved_status & ~SW_C2; /* Reduction complete. */ if ( flags & FPTAN ) { st0_ptr->exp --; return q & 7; } return (q & 3) | (flags & FCOS); }
/*--- 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; }