/*
** libgcc support for software floating point.
** Copyright (C) 1991 by Pipeline Associates, Inc.  All rights reserved.
** Permission is granted to do *anything* you want with this file,
** commercial or otherwise, provided this message remains intact.  So there!
** I would appreciate receiving any updates/patches/changes that anyone
** makes, and am willing to be the repository for said changes (am I
** making a big mistake?).

Warning! Only single-precision is actually implemented.  This file
won't really be much use until double-precision is supported.

However, once that is done, this file might eventually become a
replacement for libgcc1.c.  It might also make possible
cross-compilation for an IEEE target machine from a non-IEEE
host such as a VAX.

If you'd like to work on completing this, please talk to rms@gnu.ai.mit.edu.

--> Double precision floating support added by James Carlson on 20 April 1998.

**
** Pat Wood
** Pipeline Associates, Inc.
** pipeline!phw@motown.com or
** sun!pipeline!phw or
** uunet!motown!pipeline!phw
**
** 05/01/91 -- V1.0 -- first release to gcc mailing lists
** 05/04/91 -- V1.1 -- added float and double prototypes and return values
**                  -- fixed problems with adding and subtracting zero
**                  -- fixed rounding in truncdfsf2
**                  -- fixed SWAP define and tested on 386
*/

/*
** The following are routines that replace the libgcc soft floating point
** routines that are called automatically when -msoft-float is selected.
** The support single and double precision IEEE format, with provisions
** for byte-swapped machines (tested on 386).  Some of the double-precision
** routines work at full precision, but most of the hard ones simply punt
** and call the single precision routines, producing a loss of accuracy.
** long long support is not assumed or included.
** Overall accuracy is close to IEEE (actually 68882) for single-precision
** arithmetic.  I think there may still be a 1 in 1000 chance of a bit
** being rounded the wrong way during a multiply.  I'm not fussy enough to
** bother with it, but if anyone is, knock yourself out.
**
** Efficiency has only been addressed where it was obvious that something
** would make a big difference.  Anyone who wants to do this right for
** best speed should go in and rewrite in assembler.
**
** I have tested this only on a 68030 workstation and 386/ix integrated
** in with -msoft-float.
*/

#define float long
#define double long long

/* the following deal with IEEE single-precision numbers */
#define EXCESS          126
#define SIGNBIT         0x80000000
#define HIDDEN          (1 << 23)
#define SIGN(fp)        ((fp) & SIGNBIT)
#define EXP(fp)         (((fp) >> 23) & 0xFF)
#define MANT(fp)        (((fp) & 0x7FFFFF) | HIDDEN)
#define PACK(s,e,m)     ((s) | ((e) << 23) | (m))

/* the following deal with IEEE double-precision numbers */
#define EXCESSD         1022
#define HIDDEND         (1 << 20)
#define EXPD(fp)        (((fp.l.upper) >> 20) & 0x7FF)
#define SIGND(fp)       ((fp.l.upper) & SIGNBIT)
#define MANTD(fp)       (((((fp.l.upper) & 0xFFFFF) | HIDDEND) << 10) |(fp.l.lower >> 22))
#define HIDDEND_LL      ((long long)1 << 52)
#define MANTD_LL(fp)    ((fp.ll & (HIDDEND_LL-1)) | HIDDEND_LL)
#define PACKD_LL(s,e,m) (((long long)((s)+((e)<<20))<<32)|(m))

/* define SWAP for 386/960 reverse-byte-order brain-damaged CPUs */
union double_long {
    double d;
#ifdef SWAP
    struct {
      unsigned long lower;
      long upper;
    } l;
#else
    struct {
      long upper;
      unsigned long lower;
    } l;
#endif
    long long ll;
};

union float_long
  {
    float f;
    long l;
  };

long long
__negdi2 (long long u)
{

  union lll {
    long long ll;
    long s[2];
  };

  union lll w,uu;

  uu.ll = u;

  w.s[1] = -uu.s[1];
  w.s[0] = -uu.s[0] - ((int) w.s[1] != 0);

  return w.ll;
}



/* add two floats */
float
__addsf3 (float a1, float a2)
{
  register long mant1, mant2;
  register union float_long fl1, fl2;
  register int exp1, exp2;
  int sign = 0;

  fl1.f = a1;
  fl2.f = a2;

  /* check for zero args */
  if (!fl1.l) {
      fl1.f = fl2.f;
      goto test_done;
  }
  if (!fl2.l)
      goto test_done;

  exp1 = EXP (fl1.l);
  exp2 = EXP (fl2.l);

  if (exp1 > exp2 + 25)
      goto test_done;
  if (exp2 > exp1 + 25) {
      fl1.f = fl2.f;
      goto test_done;
  }

  /* do everything in excess precision so's we can round later */
  mant1 = MANT (fl1.l) << 6;
  mant2 = MANT (fl2.l) << 6;

  if (SIGN (fl1.l))
    mant1 = -mant1;
  if (SIGN (fl2.l))
    mant2 = -mant2;

  if (exp1 > exp2)
    {
      mant2 >>= exp1 - exp2;
    }
  else
    {
      mant1 >>= exp2 - exp1;
      exp1 = exp2;
    }
  mant1 += mant2;

  if (mant1 < 0)
    {
      mant1 = -mant1;
      sign = SIGNBIT;
    }
  else if (!mant1) {
      fl1.f = 0;
      goto test_done;
  }

  /* normalize up */
  while (!(mant1 & 0xE0000000))
    {
      mant1 <<= 1;
      exp1--;
    }

  /* normalize down? */
  if (mant1 & (1 << 30))
    {
      mant1 >>= 1;
      exp1++;
    }

  /* round to even */
  mant1 += (mant1 & 0x40) ? 0x20 : 0x1F;

  /* normalize down? */
  if (mant1 & (1 << 30))
    {
      mant1 >>= 1;
      exp1++;
    }

  /* lose extra precision */
  mant1 >>= 6;

  /* turn off hidden bit */
  mant1 &= ~HIDDEN;

  /* pack up and go home */
  fl1.l = PACK (sign, exp1, mant1);
test_done:
  return (fl1.f);
}

/* subtract two floats */
float
__subsf3 (float a1, float a2)
{
  register union float_long fl1, fl2;

  fl1.f = a1;
  fl2.f = a2;

  /* check for second arg zero */
  if (!fl2.l)
    return (fl1.f);
  /* twiddle sign bit */
  fl2.l ^= SIGNBIT;
  /* check for first arg zero */
  if (!fl1.l)
    return (fl2.f);
  /* add values */ 
  return __addsf3 (a1, fl2.f);
}

/* compare two floats */
long
__cmpsf2 (float a1, float a2)
{
  register union float_long fl1, fl2;

  fl1.f = a1;
  fl2.f = a2;

  if (SIGN (fl1.l) && SIGN (fl2.l))
    {
      fl1.l ^= SIGNBIT;
      fl2.l ^= SIGNBIT;
      if (fl1.l < fl2.l)
	return (-1);
      if (fl1.l > fl2.l)
	return (1);
      return 0;
    } else {
      if (fl1.l < fl2.l)
	return (-1);
      if (fl1.l > fl2.l)
	return (1);
      return (0);
    }
}

/* multiply two floats */
float
__mulsf3 (float a1, float a2)
{
  register union float_long fl1, fl2;
  register unsigned long result;
  register int exp;
  int sign;

  fl1.f = a1;
  fl2.f = a2;

  if (!fl1.l || !fl2.l) {
      fl1.f = 0;
      goto test_done;
  }

  /* compute sign and exponent */
  sign = SIGN (fl1.l) ^ SIGN (fl2.l);
  exp = EXP (fl1.l) - EXCESS;
  exp += EXP (fl2.l);

  fl1.l = MANT (fl1.l);
  fl2.l = MANT (fl2.l);

  /* the multiply is done as one 16x16 multiply and two 16x8 multiples */
  result = (fl1.l >> 8) * (fl2.l >> 8);
  result += ((fl1.l & 0xFF) * (fl2.l >> 8)) >> 8;
  result += ((fl2.l & 0xFF) * (fl1.l >> 8)) >> 8;

  result >>= 2;
  if (result & 0x20000000)
    {
      /* round */
      result += 0x20;
      result >>= 6;
    }
  else
    {
      /* round */
      result += 0x10;
      result >>= 5;
      exp--;
    }
  if (result & (HIDDEN<<1)) {
    result >>= 1;
    exp++;
  }

  result &= ~HIDDEN;

  /* pack up and go home */
  fl1.l = PACK (sign, exp, result);
test_done:
  return (fl1.f);
}

/* divide two floats */
float
__divsf3 (float a1, float a2)
{
  register union float_long fl1, fl2;
  register int result;
  register int mask;
  register int exp, sign;

  fl1.f = a1;
  fl2.f = a2;

  /* subtract exponents */
  exp = EXP (fl1.l) - EXP (fl2.l) + EXCESS;

  /* compute sign */
  sign = SIGN (fl1.l) ^ SIGN (fl2.l);

  /* divide by zero??? */
  if (!fl2.l)
    /* return NaN or -NaN */
    return (sign ? 0xFFFFFFFF : 0x7FFFFFFF);

  /* numerator zero??? */
  if (!fl1.l)
    return (0);

  /* now get mantissas */
  fl1.l = MANT (fl1.l);
  fl2.l = MANT (fl2.l);

  /* this assures we have 25 bits of precision in the end */
  if (fl1.l < fl2.l)
    {
      fl1.l <<= 1;
      exp--;
    }

  /* now we perform repeated subtraction of fl2.l from fl1.l */
  mask = 0x1000000;
  result = 0;
  while (mask)
    {
      if (fl1.l >= fl2.l)
        {
          result |= mask;
          fl1.l -= fl2.l;
        }
      fl1.l <<= 1;
      mask >>= 1;
    }

  /* round */
  result += 1;

  /* normalize down */
  exp++;
  result >>= 1;

  result &= ~HIDDEN;

  /* pack up and go home */
  fl1.l = PACK (sign, exp, result);
  return (fl1.f);
}

/* convert double to float */
float
__truncdfsf2 (double a1)
{
  register int exp;
  register long mant;
  register union float_long fl;
  register union double_long dl1;

  dl1.d = a1;

  if (!dl1.l.upper && !dl1.l.lower)
    return (float)(0);

  exp = EXPD (dl1) - EXCESSD + EXCESS;

  /* shift double mantissa 6 bits so we can round */
  mant = MANTD (dl1) >> 6;

  /* now round and shift down */
  mant += 1;
  mant >>= 1;
   
  /* did the round overflow? */
  if (mant & 0xFF000000)
    {
      mant >>= 1;
      exp++;
    }

  mant &= ~HIDDEN;

  /* pack up and go home */
  fl.l = PACK (SIGND (dl1), exp, mant);
  return (fl.f);
}

/* convert int to double */
double
__floatsidf (register long a1)
{
  register int sign = 0, exp = 31 + EXCESSD;
  union double_long dl;

  if (a1 == 0x80000000)
    {
      /*
       * -a1 would be 0 !
       */
      dl.l.upper = 0xc1e00000;
      dl.l.lower = 0x0;
      return (dl.d);
    }

  if (!a1)
    {
      dl.l.upper = dl.l.lower = 0;
      return (dl.d);
    }

  if (a1 < 0)
    {
      sign = SIGNBIT;
      a1 = -a1;
    }

  while (a1 < 0x1000000)
    {
      a1 <<= 4;
      exp -= 4;
    }

  while (a1 < 0x40000000)
    {
      a1 <<= 1;
      exp--;
    }

  /* pack up and go home */
  dl.l.upper = sign;
  dl.l.upper |= exp << 20;
  dl.l.upper |= (a1 >> 10) & ~HIDDEND;
  dl.l.lower = a1 << 22;

  return (dl.d);
}

double
__floatdidf (register long long a1)
{
    register int exp = 63 + EXCESSD;
    union double_long dl;

    dl.l.upper = dl.l.lower = 0;
    if (a1 == 0)
        return (dl.d);

    if (a1 < 0) {
        dl.l.upper = SIGNBIT;
        a1 = -a1;
    }

    while (a1 < (long long)1<<54) {
        a1 <<= 8;
        exp -= 8;
    }
    while (a1 < (long long)1<<62) {
        a1 <<= 1;
        exp -= 1;
    }
  /* pack up and go home */
    dl.ll |= (a1 >> 10) & ~HIDDEND_LL;
    dl.l.upper |= exp << 20;

    return (dl.d);
}

float
__floatsisf (register long a1)
{
    return __truncdfsf2(__floatsidf(a1));
}

float
__floatdisf (register long long a1)
{
    return (float)__floatdidf(a1);
}
/* negate a float */
float
__negsf2 (float a1)
{
  register union float_long fl1;

  fl1.f = a1;
  if (!fl1.l)
    return (0);

  fl1.l ^= SIGNBIT;
  return (fl1.f);
}

/* negate a double */
double
__negdf2 (double a1)
{
  register union double_long dl1;

  dl1.d = a1;

  if (!dl1.l.upper && !dl1.l.lower)
      return (dl1.d);

  dl1.l.upper ^= SIGNBIT;
  return (dl1.d);
}

/* convert float to double */
double
__extendsfdf2 (float a1)
{
  register union float_long fl1;
  register union double_long dl;
  register int exp;

  fl1.f = a1;

  if (!fl1.l)
    {
      dl.l.upper = dl.l.lower = 0;
      return (dl.d);
    }

  dl.l.upper = SIGN (fl1.l);
  exp = EXP (fl1.l) - EXCESS + EXCESSD;
  dl.l.upper |= exp << 20;
  dl.l.upper |= (MANT (fl1.l) & ~HIDDEN) >> 3;
  dl.l.lower = MANT (fl1.l) << 29;

  return (dl.d);
}


/* compare two doubles */
long
__cmpdf2 (double a1, double a2)
{
  register union double_long dl1, dl2;

  dl1.d = a1;
  dl2.d = a2;

  if (SIGND (dl1) && SIGND (dl2))
    {
      dl1.l.upper ^= SIGNBIT;
      dl2.l.upper ^= SIGNBIT;
      if (dl1.l.upper < dl2.l.upper)
	return (1);
      if (dl1.l.upper > dl2.l.upper)
	return (-1);
      if (dl1.l.lower < dl2.l.lower)
	return (1);
      if (dl1.l.lower > dl2.l.lower)
	return (-1);
      return (0);
    } else {
      if (dl1.l.upper < dl2.l.upper)
	return (-1);
      if (dl1.l.upper > dl2.l.upper)
	return (1);
      if (dl1.l.lower < dl2.l.lower)
	return (-1);
      if (dl1.l.lower > dl2.l.lower)
	return (1);
      return (0);
    }
}

/* convert double to int */
long
__fixdfsi (double a1)
{
  register union double_long dl1;
  register int exp;
  register long l;

  dl1.d = a1;

  if (!dl1.l.upper && !dl1.l.lower)
    return (0);

  exp = EXPD (dl1) - EXCESSD - 31;
  l = MANTD (dl1);

  if (exp > 0)
      return SIGND(dl1) ? (1<<31) : ((1ul<<31)-1);

  /* shift down until exp = 0 or l = 0 */
  if (exp <= 0 && exp > -32 && l)
    l >>= -exp;
  else
    return (0);

  return (SIGND (dl1) ? -l : l);
}

/* convert float to int */
long
__fixsfsi (float a1)
{
    return __fixdfsi(__extendsfdf2(a1));
}

/* convert double to int */
long long
__fixdfdi (double a1)
{
    register union double_long dl1;
    register int exp;
    register long long l;

    dl1.d = a1;

    if (!dl1.l.upper && !dl1.l.lower)
        return (0);

    exp = EXPD (dl1) - EXCESSD - 64;
    l = MANTD_LL(dl1);

    if (exp > 0) {
        l = (long long)1<<63;
        if (!SIGND(dl1))
            l--;
        return l;
    }

    /* shift down until exp = 0 or l = 0 */
    if (exp <= 0 && exp > -64 && l)
        l >>= -exp;
    else
        return (0);

    return (SIGND (dl1) ? -l : l);
}

/* convert double to unsigned int */
unsigned long
__fixunsdfsi (double a1)
{
  register union double_long dl1;
  register int exp;
  register unsigned long l;

  dl1.d = a1;

  if (!dl1.l.upper && !dl1.l.lower)
    return (0);

  exp = EXPD (dl1) - EXCESSD - 32;
  l = (((((dl1.l.upper) & 0xFFFFF) | HIDDEND) << 11) | (dl1.l.lower >> 21));

  if (exp > 0)
    return (0xFFFFFFFFul);      /* largest integer */

  /* shift down until exp = 0 or l = 0 */
  if (exp < 0 && exp > -32 && l)
    l >>= -exp;
  else
    return (0);

  return (l);
}

/* convert double to unsigned int */
unsigned long long
__fixunsdfdi (double a1)
{
    register union double_long dl1;
    register int exp;
    register unsigned long long l;

    dl1.d = a1;

    if (dl1.ll == 0)
        return (0);

    exp = EXPD (dl1) - EXCESSD - 64;

    l = dl1.ll;

    if (exp > 0)
        return (unsigned long long)-1;

    /* shift down until exp = 0 or l = 0 */
    if (exp < 0 && exp > -64 && l)
        l >>= -exp;
    else
        return (0);

    return (l);
}

/* addtwo doubles */
double
__adddf3 (double a1, double a2)
{
    register long long mant1, mant2;
    register union double_long fl1, fl2;
    register int exp1, exp2;
    int sign = 0;

    fl1.d = a1;
    fl2.d = a2;

    /* check for zero args */
    if (!fl2.ll)
        goto test_done;
    if (!fl1.ll) {
        fl1.d = fl2.d;
        goto test_done;
    }

    exp1 = EXPD(fl1);
    exp2 = EXPD(fl2);

    if (exp1 > exp2 + 54)
        goto test_done;
    if (exp2 > exp1 + 54) {
        fl1.d = fl2.d;
        goto test_done;
    }

    /* do everything in excess precision so's we can round later */
    mant1 = MANTD_LL(fl1) << 9;
    mant2 = MANTD_LL(fl2) << 9;

    if (SIGND(fl1))
        mant1 = -mant1;
    if (SIGND(fl2))
        mant2 = -mant2;

    if (exp1 > exp2)
        mant2 >>= exp1 - exp2;
    else {
        mant1 >>= exp2 - exp1;
        exp1 = exp2;
    }
    mant1 += mant2;

    if (mant1 < 0) {
        mant1 = -mant1;
        sign = SIGNBIT;
    } else if (!mant1) {
        fl1.d = 0;
        goto test_done;
    }

    /* normalize up */
    while (!(mant1 & ((long long)7<<61))) {
        mant1 <<= 1;
        exp1--;
    }

    /* normalize down? */
    if (mant1 & ((long long)3<<62)) {
        mant1 >>= 1;
        exp1++;
    }

    /* round to even */
    mant1 += (mant1 & (1<<9)) ? (1<<8) : ((1<<8)-1);

    /* normalize down? */
    if (mant1 & ((long long)3<<62)) {
        mant1 >>= 1;
        exp1++;
    }

    /* lose extra precision */
    mant1 >>= 9;

    /* turn off hidden bit */
    mant1 &= ~HIDDEND_LL;

    /* pack up and go home */
    fl1.ll = PACKD_LL(sign,exp1,mant1);

test_done:
    return (fl1.d);
}

/* subtract two doubles */
double
__subdf3 (double a1, double a2)
{
    register union double_long fl1, fl2;

    fl1.d = a1;
    fl2.d = a2;

    /* check for zero args */
    if (!fl2.ll)
        return (fl1.d);
    /* twiddle sign bit and add */
    fl2.l.upper ^= SIGNBIT;
    if (!fl1.ll)
        return (fl2.d);
    return __adddf3 (a1, fl2.d);
}

/* multiply two doubles */
double
__muldf3 (double a1, double a2)
{
    register union double_long fl1, fl2;
    register unsigned long long result=0ULL;
    register int exp;
    int sign;

    fl1.d = a1;
    fl2.d = a2;

    if (!fl1.ll || !fl2.ll) {
        fl1.d = 0;
        goto test_done;
    }

    /* compute sign and exponent */
    sign = SIGND(fl1) ^ SIGND(fl2);
    exp = EXPD(fl1) - EXCESSD;
    exp += EXPD(fl2);

    fl1.ll = MANTD_LL(fl1);
    fl2.ll = MANTD_LL(fl2);

  /* the multiply is done as one 31x31 multiply and two 31x21 multiples */
    result = (fl1.ll >> 21) * (fl2.ll >> 21);
    result += ((fl1.ll & 0x1FFFFF) * (fl2.ll >> 21)) >> 21;
    result += ((fl2.ll & 0x1FFFFF) * (fl1.ll >> 21)) >> 21;

    result >>= 2;
    if (result & ((long long)1<<61)) {
        /* round */
        result += 1<<8;
        result >>= 9;
    } else {
        /* round */
        result += 1<<7;
        result >>= 8;
        exp--;
    }
    if (result & (HIDDEND_LL<<1)) {
        result >>= 1;
        exp++;
    }

    result &= ~HIDDEND_LL;

    /* pack up and go home */
    fl1.ll = PACKD_LL(sign,exp,result);
test_done:
    return (fl1.d);
}

/* divide two doubles */
double
__divdf3 (double a1, double a2)
{
    register union double_long fl1, fl2;
    register long long mask,result;
    register int exp, sign;

    fl1.d = a1;
    fl2.d = a2;

    /* subtract exponents */
    exp = EXPD(fl1) - EXPD(fl2) + EXCESSD;

    /* compute sign */
    sign = SIGND(fl1) ^ SIGND(fl2);

    /* numerator zero??? */
    if (fl1.ll == 0) {
        /* divide by zero??? */
        if (fl2.ll == 0)
            fl1.ll = ((unsigned long long)1<<63)-1;     /* NaN */
        else
            fl1.ll = 0;
        goto test_done;
    }

    /* return +Inf or -Inf */
    if (fl2.ll == 0) {
        fl1.ll = PACKD_LL(SIGND(fl1),2047,0);
        goto test_done;
    }


    /* now get mantissas */
    fl1.ll = MANTD_LL(fl1);
    fl2.ll = MANTD_LL(fl2);

    /* this assures we have 54 bits of precision in the end */
    if (fl1.ll < fl2.ll) {
        fl1.ll <<= 1;
        exp--;
    }

    /* now we perform repeated subtraction of fl2.ll from fl1.ll */
    mask = (long long)1<<53;
    result = 0;
    while (mask) {
        if (fl1.ll >= fl2.ll)
        {
            result |= mask;
            fl1.ll -= fl2.ll;
        }
        fl1.ll <<= 1;
        mask >>= 1;
    }

    /* round */
    result += 1;

    /* normalize down */
    exp++;
    result >>= 1;

    result &= ~HIDDEND_LL;

    /* pack up and go home */
    fl1.ll = PACKD_LL(sign, exp, result);

test_done:
    return (fl1.d);
}

int
__gtdf2 (double a1, double a2)
{
    return __cmpdf2 ((float) a1, (float) a2) > 0;
}

int
__gedf2 (double a1, double a2)
{
    return (__cmpdf2 ((float) a1, (float) a2) >= 0) - 1;
}

int
__ltdf2 (double a1, double a2)
{
    return - (__cmpdf2 ((float) a1, (float) a2) < 0);
}

int
__ledf2 (double a1, double a2)
{
    return __cmpdf2 ((float) a1, (float) a2) > 0;
}

int
__eqdf2 (double a1, double a2)
{
    return *(long long *) &a1 == *(long long *) &a2;
}

int
__nedf2 (double a1, double a2)
{
    return *(long long *) &a1 != *(long long *) &a2;
}

/* absolute value of double */
double
__absdf2(double a1)
{
  if (__cmpdf2(a1,0.0) < 0)
      return __negdf2(a1);
  else
      return a1;
}

/* absolute value of float */
float
__abssf2(float a1)
{
  if (__cmpsf2(a1,0.0) < 0)
      return __negsf2(a1);
  else
      return a1;
}
