int bfdec_divrem()

in GaiaXAndroidQuickJS/quickjs/gxquickjs/libbf.c [6949:7052]


int bfdec_divrem(bfdec_t *q, bfdec_t *r, const bfdec_t *a, const bfdec_t *b,
                 limb_t prec, bf_flags_t flags, int rnd_mode)
{
    bf_context_t *s = q->ctx;
    bfdec_t a1_s, *a1 = &a1_s;
    bfdec_t b1_s, *b1 = &b1_s;
    bfdec_t r1_s, *r1 = &r1_s;
    int q_sign, res;
    BOOL is_ceil, is_rndn;
    
    assert(q != a && q != b);
    assert(r != a && r != b);
    assert(q != r);
    
    if (a->len == 0 || b->len == 0) {
        bfdec_set_zero(q, 0);
        if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) {
            bfdec_set_nan(r);
            return 0;
        } else if (a->expn == BF_EXP_INF || b->expn == BF_EXP_ZERO) {
            bfdec_set_nan(r);
            return BF_ST_INVALID_OP;
        } else {
            bfdec_set(r, a);
            return bfdec_round(r, prec, flags);
        }
    }

    q_sign = a->sign ^ b->sign;
    is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA);
    switch(rnd_mode) {
    default:
    case BF_RNDZ:
    case BF_RNDN:
    case BF_RNDNA:
        is_ceil = FALSE;
        break;
    case BF_RNDD:
        is_ceil = q_sign;
        break;
    case BF_RNDU:
        is_ceil = q_sign ^ 1;
        break;
    case BF_RNDA:
        is_ceil = TRUE;
        break;
    case BF_DIVREM_EUCLIDIAN:
        is_ceil = a->sign;
        break;
    }

    a1->expn = a->expn;
    a1->tab = a->tab;
    a1->len = a->len;
    a1->sign = 0;
    
    b1->expn = b->expn;
    b1->tab = b->tab;
    b1->len = b->len;
    b1->sign = 0;

    //    bfdec_print_str("a1", a1);
    //    bfdec_print_str("b1", b1);
    /* XXX: could improve to avoid having a large 'q' */
    bfdec_tdivremu(s, q, r, a1, b1);
    if (bfdec_is_nan(q) || bfdec_is_nan(r))
        goto fail;
    //    bfdec_print_str("q", q);
    //    bfdec_print_str("r", r);
    
    if (r->len != 0) {
        if (is_rndn) {
            bfdec_init(s, r1);
            if (bfdec_set(r1, r))
                goto fail;
            if (bfdec_mul_si(r1, r1, 2, BF_PREC_INF, BF_RNDZ)) {
                bfdec_delete(r1);
                goto fail;
            }
            res = bfdec_cmpu(r1, b);
            bfdec_delete(r1);
            if (res > 0 ||
                (res == 0 &&
                 (rnd_mode == BF_RNDNA ||
                  (get_digit(q->tab, q->len, q->len * LIMB_DIGITS - q->expn) & 1) != 0))) {
                goto do_sub_r;
            }
        } else if (is_ceil) {
        do_sub_r:
            res = bfdec_add_si(q, q, 1, BF_PREC_INF, BF_RNDZ);
            res |= bfdec_sub(r, r, b1, BF_PREC_INF, BF_RNDZ);
            if (res & BF_ST_MEM_ERROR)
                goto fail;
        }
    }

    r->sign ^= a->sign;
    q->sign = q_sign;
    return bfdec_round(r, prec, flags);
 fail:
    bfdec_set_nan(q);
    bfdec_set_nan(r);
    return BF_ST_MEM_ERROR;
}