/*
 *  call-seq:
  *     big.fdiv(numeric) -> float
 *
 *  Returns the floating point result of dividing <i>big</i> by
 *  <i>numeric</i>.
 *
 *     -1234567890987654321.fdiv(13731)      #=> -89910996357705.5
 *     -1234567890987654321.fdiv(13731.24)   #=> -89909424858035.7
 *
 */

static VALUE
rb_big_fdiv(VALUE x, VALUE y)
{
    double dx = big2dbl(x);
    double dy;

    if (isinf(dx)) {
#define DBL_BIGDIG ((DBL_MANT_DIG + BITSPERDIG) / BITSPERDIG)
        VALUE z;
        int ex, ey;

        ex = (RBIGNUM_LEN(bigtrunc(x)) - 1) * BITSPERDIG;
        ex += bdigbitsize(BDIGITS(x)[RBIGNUM_LEN(x) - 1]);
        ex -= 2 * DBL_BIGDIG * BITSPERDIG;
        if (ex) x = big_shift(x, ex);

        switch (TYPE(y)) {
          case T_FIXNUM:
            y = rb_int2big(FIX2LONG(y));
          case T_BIGNUM: {
            ey = (RBIGNUM_LEN(bigtrunc(y)) - 1) * BITSPERDIG;
            ey += bdigbitsize(BDIGITS(y)[RBIGNUM_LEN(y) - 1]);
            ey -= DBL_BIGDIG * BITSPERDIG;
            if (ey) y = big_shift(y, ey);
          bignum:
            bigdivrem(x, y, &z, 0);
            return DOUBLE2NUM(ldexp(big2dbl(z), ex - ey));
          }
          case T_FLOAT:
            if (isnan(RFLOAT_VALUE(y))) return y;
            y = dbl2big(ldexp(frexp(RFLOAT_VALUE(y), &ey), DBL_MANT_DIG));
            ey -= DBL_MANT_DIG;
            goto bignum;
        }
    }
    switch (TYPE(y)) {
      case T_FIXNUM:
        dy = (double)FIX2LONG(y);
        break;

      case T_BIGNUM:
        dy = rb_big2dbl(y);
        break;

      case T_FLOAT:
        dy = RFLOAT_VALUE(y);
        break;

      default:
        return rb_num_coerce_bin(x, y, rb_intern("fdiv"));
    }
    return DOUBLE2NUM(dx / dy);
}