/* The argument to this function must be polynomial() compatible, i.e. have an exponent (not checked) of EXP_BIAS-1 but need not be normalized. This function adds 1.0 to the (assumed positive) argument. */ void poly_add_1(FPU_REG * src) { /* Rounding in a consistent direction produces better results for the use of this function in poly_atan. Simple truncation is used here instead of round-to-nearest. */ #ifdef OBSOLETE char round = (src->sigl & 3) == 3; #endif /* OBSOLETE */ shrx(&src->sigl, 1); #ifdef OBSOLETE if (round) (*(long long *) &src->sigl)++; /* Round to even */ #endif /* OBSOLETE */ src->sigh |= 0x80000000; src->exp = EXP_BIAS; }
/*--- poly_sine() -----------------------------------------------------------+ | | +---------------------------------------------------------------------------*/ void poly_sine(FPU_REG const *arg, FPU_REG *result) { short exponent; FPU_REG fixed_arg, arg_sqrd, arg_to_4, accum, negaccum; exponent = arg->exp - EXP_BIAS; if ( arg->tag == TW_Zero ) { /* Return 0.0 */ reg_move(&CONST_Z, result); return; } #ifdef PARANOID if ( arg->sign != 0 ) /* Can't hack a number < 0.0 */ { EXCEPTION(EX_Invalid); reg_move(&CONST_QNaN, result); return; } if ( exponent >= 0 ) /* Can't hack a number > 1.0 */ { if ( (exponent == 0) && (arg->sigl == 0) && (arg->sigh == 0x80000000) ) { reg_move(&CONST_1, result); return; } EXCEPTION(EX_Invalid); reg_move(&CONST_QNaN, result); return; } #endif PARANOID fixed_arg.sigl = arg->sigl; fixed_arg.sigh = arg->sigh; if ( exponent < -1 ) { /* shift the argument right by the required places */ if ( shrx(&(fixed_arg.sigl), -1-exponent) >= 0x80000000U ) significand(&fixed_arg)++; /* round up */ } mul64(&significand(&fixed_arg), &significand(&fixed_arg), &significand(&arg_sqrd)); mul64(&significand(&arg_sqrd), &significand(&arg_sqrd), &significand(&arg_to_4)); /* will be a valid positive nr with expon = 0 */ *(short *)&(accum.sign) = 0; accum.exp = 0; /* Do the basic fixed point polynomial evaluation */ polynomial(&(accum.sigl), &(arg_to_4.sigl), lterms, HIPOWER-1); /* will be a valid positive nr with expon = 0 */ *(short *)&(negaccum.sign) = 0; negaccum.exp = 0; /* Do the basic fixed point polynomial evaluation */ polynomial(&(negaccum.sigl), &(arg_to_4.sigl), negterms, HIPOWER-1); mul64(&significand(&arg_sqrd), &significand(&negaccum), &significand(&negaccum)); /* Subtract the mantissas */ significand(&accum) -= significand(&negaccum); /* Convert to 64 bit signed-compatible */ accum.exp = EXP_BIAS - 1 + accum.exp; reg_move(&accum, result); normalize(result); reg_mul(result, arg, result, FULL_PRECISION); reg_u_add(result, arg, result, FULL_PRECISION); if ( result->exp >= EXP_BIAS ) { /* A small overflow may be possible... but an illegal result. */ if ( (result->exp > EXP_BIAS) /* Larger or equal 2.0 */ || (result->sigl > 1) /* Larger than 1.0+msb */ || (result->sigh != 0x80000000) /* Much > 1.0 */ ) { #ifdef DEBUGGING RE_ENTRANT_CHECK_OFF; printk("\nEXP=%d, MS=%08x, LS=%08x\n", result->exp, result->sigh, result->sigl); RE_ENTRANT_CHECK_ON; #endif DEBUGGING EXCEPTION(EX_INTERNAL|0x103); } #ifdef DEBUGGING RE_ENTRANT_CHECK_OFF; printk("\n***CORRECTING ILLEGAL RESULT*** in poly_sin() computation\n"); printk("EXP=%d, MS=%08x, LS=%08x\n", result->exp, result->sigh, result->sigl); RE_ENTRANT_CHECK_ON; #endif DEBUGGING result->sigl = 0; /* Truncate the result to 1.00 */ } }
/*--- 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); }
/*--- 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); } }
/*--- poly_tan() ------------------------------------------------------------+ | | +---------------------------------------------------------------------------*/ void poly_tan(FPU_REG const *arg, FPU_REG *result) { long int exponent; int invert; Xsig argSq, argSqSq, accumulatoro, accumulatore, accum, argSignif, fix_up; unsigned long adj; exponent = arg->exp - EXP_BIAS; #ifdef PARANOID if ( arg->sign != 0 ) /* Can't hack a number < 0.0 */ { arith_invalid(result); return; } /* Need a positive number */ #endif PARANOID /* Split the problem into two domains, smaller and larger than pi/4 */ if ( (exponent == 0) || ((exponent == -1) && (arg->sigh > 0xc90fdaa2)) ) { /* The argument is greater than (approx) pi/4 */ invert = 1; accum.lsw = 0; XSIG_LL(accum) = significand(arg); 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); 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(arg); if ( exponent < -1 ) { /* shift the argument right by the required places */ if ( 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 */ mul_32_32(adj, adj, &adj); /* tan^2 */ } else