Skip to content

Commit

Permalink
Fix the result precision of BigDecimal#divmod
Browse files Browse the repository at this point in the history
  • Loading branch information
mrkn committed Dec 23, 2021
1 parent c3453d2 commit a32f6cb
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 6 deletions.
21 changes: 17 additions & 4 deletions ext/bigdecimal/bigdecimal.c
Original file line number Diff line number Diff line change
Expand Up @@ -1618,7 +1618,8 @@ BigDecimal_divide(VALUE self, VALUE r, Real **c, Real **res, Real **div)
/* For c = self.div(r): with round operation */
{
ENTER(5);
Real *a, *b;
Real *a, *b, *d;
ssize_t a_prec, b_prec;
size_t mx;

TypedData_Get_Struct(self, Real, &BigDecimal_data_type, a);
Expand All @@ -1644,18 +1645,27 @@ BigDecimal_divide(VALUE self, VALUE r, Real **c, Real **res, Real **div)

TypedData_Get_Struct(rr, Real, &BigDecimal_data_type, b);
SAVE(b);

*div = b;

mx = (a->Prec > b->Prec) ? a->Prec : b->Prec;
mx *= BASE_FIG;

BigDecimal_count_precision_and_scale(self, &a_prec, NULL);
BigDecimal_count_precision_and_scale(rr, &b_prec, NULL);
mx = (a_prec > b_prec) ? a_prec : b_prec;

if (2*BIGDECIMAL_DOUBLE_FIGURES > mx)
mx = 2*BIGDECIMAL_DOUBLE_FIGURES;

GUARD_OBJ((*c), VpCreateRbObject(mx + 2*BASE_FIG, "#0", true));
GUARD_OBJ((*res), VpCreateRbObject(mx*2 + 2*BASE_FIG, "#0", true));
VpDivd(*c, *res, a, b);

return Qnil;
}

static VALUE BigDecimal_DoDivmod(VALUE self, VALUE r, Real **div, Real **mod);

/* call-seq:
* a / b -> bigdecimal
*
Expand Down Expand Up @@ -1736,6 +1746,7 @@ BigDecimal_DoDivmod(VALUE self, VALUE r, Real **div, Real **mod)
ENTER(8);
Real *c=NULL, *d=NULL, *res=NULL;
Real *a, *b;
ssize_t a_prec, b_prec;
size_t mx;

TypedData_Get_Struct(self, Real, &BigDecimal_data_type, a);
Expand Down Expand Up @@ -1793,8 +1804,10 @@ BigDecimal_DoDivmod(VALUE self, VALUE r, Real **div, Real **mod)
return Qtrue;
}

mx = (a->Prec > b->Prec) ? a->Prec : b->Prec;
mx *= BASE_FIG;
BigDecimal_count_precision_and_scale(self, &a_prec, NULL);
BigDecimal_count_precision_and_scale(rr, &b_prec, NULL);

mx = (a_prec > b_prec) ? a_prec : b_prec;
if (2*BIGDECIMAL_DOUBLE_FIGURES > mx)
mx = 2*BIGDECIMAL_DOUBLE_FIGURES;

Expand Down
6 changes: 4 additions & 2 deletions test/bigdecimal/test_bigdecimal.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1047,11 +1047,13 @@ def test_divmod_precision
a = BigDecimal('2e55')
b = BigDecimal('1.23456789e10')
q, r = a.divmod(b)
assert_equal((a/b), q)
assert_equal((a/b).round(0, :down), q)
assert_equal((a - q*b), r)

b = BigDecimal('-1.23456789e10')
q, r = a.divmod(b)
assert_equal((a/b), q)
assert_equal((a/b).round(0, :down) - 1, q)
assert_equal((a - q*b), r)
end

def test_divmod_error
Expand Down

0 comments on commit a32f6cb

Please sign in to comment.