Ejemplo n.º 1
0
/*--- 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 */
    }

}
Ejemplo n.º 2
0
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
}
Ejemplo n.º 3
0
/*--- 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);
	}

}
Ejemplo n.º 4
0
/* 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
}