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; }
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 f2xm1(FPU_REG *st0_ptr, u_char tag) { FPU_REG a; clear_C1(); if ( tag == TAG_Valid ) { /* For an 80486 FPU, the result is undefined if the arg is >= 1.0 */ if ( exponent(st0_ptr) < 0 ) { denormal_arg: FPU_to_exp16(st0_ptr, &a); /* poly_2xm1(x) requires 0 < st(0) < 1. */ poly_2xm1(getsign(st0_ptr), &a, st0_ptr); } set_precision_flag_up(); /* 80486 appears to always do this */ return; } if ( tag == TAG_Zero ) return; if ( tag == TAG_Special ) tag = FPU_Special(st0_ptr); switch ( tag ) { case TW_Denormal: if ( denormal_operand() < 0 ) return; goto denormal_arg; case TW_Infinity: if ( signnegative(st0_ptr) ) { /* -infinity gives -1 (p16-10) */ FPU_copy_to_reg0(&CONST_1, TAG_Valid); setnegative(st0_ptr); } return; default: single_arg_error(st0_ptr, tag); } }
static void f2xm1(FPU_REG *st0_ptr, u_char tag) { FPU_REG a; clear_C1(); if (tag == TAG_Valid) { if (exponent(st0_ptr) < 0) { denormal_arg: FPU_to_exp16(st0_ptr, &a); poly_2xm1(getsign(st0_ptr), &a, st0_ptr); } set_precision_flag_up(); return; } if (tag == TAG_Zero) return; if (tag == TAG_Special) tag = FPU_Special(st0_ptr); switch (tag) { case TW_Denormal: if (denormal_operand() < 0) return; goto denormal_arg; case TW_Infinity: if (signnegative(st0_ptr)) { FPU_copy_to_reg0(&CONST_1, TAG_Valid); setnegative(st0_ptr); } return; default: single_arg_error(st0_ptr, tag); } }
static int trig_arg(FPU_REG *st0_ptr, int even) { FPU_REG tmp; u_char tmptag; unsigned long long 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; return -1; } 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); 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(&tmp); FPU_copy_to_reg0(&tmp, st0_tag); } if ((even && !(q & 1)) || (!even && (q & 1))) { st0_tag = FPU_sub(REV | LOADED | TAG_Valid, (int)&CONST_PI2, FULL_PRECISION); #ifdef BETTER_THAN_486 if ((exponent(st0_ptr) <= exponent(&CONST_PI2extra) + 64) || (q > 1)) { significand(&tmp) = q + 1; setexponent16(&tmp, 63); FPU_normalize(&tmp); 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)) { setpositive(st0_ptr); q++; } } #endif } #ifdef BETTER_THAN_486 else { if (((q > 0) && (exponent(st0_ptr) <= exponent(&CONST_PI2extra) + 64)) || (q > 1)) { significand(&tmp) = q; setexponent16(&tmp, 63); FPU_normalize(&tmp); 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), (int)&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)))) { st0_tag = FPU_sub(REV | LOADED | TAG_Valid, (int)&CONST_PI2, FULL_PRECISION); q++; } } } #endif FPU_settag0(st0_tag); control_word = old_cw; partial_status = saved_status & ~SW_C2; return (q & 3) | even; }
/*--- 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
/* 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 even) { FPU_REG tmp; u_char tmptag; unsigned long long 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; } 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(&tmp); FPU_copy_to_reg0(&tmp, st0_tag); } if ((even && !(q & 1)) || (!even && (q & 1))) { st0_tag = FPU_sub(REV | LOADED | TAG_Valid, (int)&CONST_PI2, FULL_PRECISION); #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(&tmp); 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)) { /* 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. */ 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(&tmp); /* 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), (int)&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. */ st0_tag = FPU_sub(REV | LOADED | TAG_Valid, (int)&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. */ return (q & 3) | even; }