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; }
/* 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; }
/* 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; }
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; }