/*--- 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 */ } }
void reg_add(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w) { int diff; if ( !(a->tag | b->tag) ) { /* Both registers are valid */ if (!(a->sign ^ b->sign)) { /* signs are the same */ reg_u_add(a, b, dest, control_w); dest->sign = a->sign; return; } /* The signs are different, so do a subtraction */ diff = a->exp - b->exp; if (!diff) { diff = a->sigh - b->sigh; /* Works only if ms bits are identical */ if (!diff) { diff = a->sigl > b->sigl; if (!diff) diff = -(a->sigl < b->sigl); } } if (diff > 0) { reg_u_sub(a, b, dest, control_w); dest->sign = a->sign; } else if ( diff == 0 ) { reg_move(&CONST_Z, dest); /* sign depends upon rounding mode */ dest->sign = ((control_w & CW_RC) != RC_DOWN) ? SIGN_POS : SIGN_NEG; } else { reg_u_sub(b, a, dest, control_w); dest->sign = b->sign; } return; } else { if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) ) { real_2op_NaN(a, b, dest); return; } else if (a->tag == TW_Zero) { if (b->tag == TW_Zero) { char different_signs = a->sign ^ b->sign; /* Both are zero, result will be zero. */ reg_move(a, dest); if (different_signs) { /* Signs are different. */ /* Sign of answer depends upon rounding mode. */ dest->sign = ((control_w & CW_RC) != RC_DOWN) ? SIGN_POS : SIGN_NEG; } } else reg_move(b, dest); return; } else if (b->tag == TW_Zero) { reg_move(a, dest); return; } else if (a->tag == TW_Infinity) { if (b->tag != TW_Infinity) { reg_move(a, dest); return; } /* They are both + or - infinity */ if (a->sign == b->sign) { reg_move(a, dest); return; } reg_move(&CONST_QNaN, dest); /* inf - inf is undefined. */ return; } else if (b->tag == TW_Infinity) { reg_move(b, dest); return; } } #ifdef PARANOID EXCEPTION(EX_INTERNAL|0x101); #endif }
/*--- 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); } }
/* Subtract b from a. (a-b) -> dest */ void reg_sub(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w) { int diff; if ( !(a->tag | b->tag) ) { /* Both registers are valid */ diff = a->exp - b->exp; if (!diff) { diff = a->sigh - b->sigh; /* Works only if ms bits are identical */ if (!diff) { diff = a->sigl > b->sigl; if (!diff) diff = -(a->sigl < b->sigl); } } switch (a->sign*2 + b->sign) { case 0: /* P - P */ case 3: /* N - N */ if (diff > 0) { reg_u_sub(a, b, dest, control_w); dest->sign = a->sign; } else if ( diff == 0 ) { reg_move(&CONST_Z, dest); /* sign depends upon rounding mode */ dest->sign = ((control_w & CW_RC) != RC_DOWN) ? SIGN_POS : SIGN_NEG; } else { reg_u_sub(b, a, dest, control_w); dest->sign = a->sign ^ SIGN_POS^SIGN_NEG; } return; case 1: /* P - N */ reg_u_add(a, b, dest, control_w); dest->sign = SIGN_POS; return; case 2: /* N - P */ reg_u_add(a, b, dest, control_w); dest->sign = SIGN_NEG; return; } } else { if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) ) { real_2op_NaN(a, b, dest); return; } else if (b->tag == TW_Zero) { if (a->tag == TW_Zero) { char same_signs = !(a->sign ^ b->sign); /* Both are zero, result will be zero. */ reg_move(a, dest); /* Answer for different signs. */ if (same_signs) { /* Sign depends upon rounding mode */ dest->sign = ((control_w & CW_RC) != RC_DOWN) ? SIGN_POS : SIGN_NEG; } } else reg_move(a, dest); return; } else if (a->tag == TW_Zero) { reg_move(b, dest); dest->sign ^= SIGN_POS^SIGN_NEG; return; } else if (a->tag == TW_Infinity) { if (b->tag != TW_Infinity) { reg_move(a, dest); return; } if (a->sign == b->sign) { reg_move(&CONST_QNaN, dest); return; } reg_move(a, dest); return; } else if (b->tag == TW_Infinity) { reg_move(b, dest); dest->sign ^= SIGN_POS^SIGN_NEG; return; } } #ifdef PARANOID EXCEPTION(EX_INTERNAL|0x110); #endif }