Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions ext/bigdecimal/bigdecimal.c
Original file line number Diff line number Diff line change
Expand Up @@ -1825,7 +1825,7 @@ BigDecimal_divide(VALUE self, VALUE r, Real **c, Real **res, Real **div)
ENTER(5);
Real *a, *b;
ssize_t a_prec, b_prec;
size_t mx;
size_t mx, res_maxprec;

TypedData_Get_Struct(self, Real, &BigDecimal_data_type, a);
SAVE(a);
Expand Down Expand Up @@ -1854,14 +1854,15 @@ BigDecimal_divide(VALUE self, VALUE r, Real **c, Real **res, Real **div)

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;
mx *= 2;
mx = ((a_prec > b_prec) ? a_prec : b_prec) + BIGDECIMAL_DOUBLE_FIGURES;

if (2*BIGDECIMAL_DOUBLE_FIGURES > mx)
mx = 2*BIGDECIMAL_DOUBLE_FIGURES;
Comment on lines +1857 to 1860

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Precision was max(a_prec*2, b_prec*2, 32).
I think we normally need max(a_prec, b_prec) + extra precision, and the extra part was ≧16 before.


GUARD_OBJ((*c), NewZeroWrapNolimit(1, mx + 2*BASE_FIG));
GUARD_OBJ((*res), NewZeroWrapNolimit(1, (mx + 1)*2 + 2*BASE_FIG));
res_maxprec = b->Prec + (*c)->MaxPrec - 1;
if (res_maxprec <= a->Prec) res_maxprec = a->Prec + 1;
GUARD_OBJ((*res), NewZeroWrapNolimit(1, res_maxprec * BASE_FIG));
VpDivd(*c, *res, a, b);

return Qnil;
Expand Down Expand Up @@ -2218,9 +2219,9 @@ BigDecimal_div2(VALUE self, VALUE b, VALUE n)
b_prec = BIGDECIMAL_DOUBLE_FIGURES;
}
GUARD_OBJ(bv, GetVpValueWithPrec(b, b_prec, 1));
mx = av->Prec + bv->Prec + 2;
if (mx <= cv->MaxPrec) mx = cv->MaxPrec + 1;
GUARD_OBJ(res, NewZeroWrapNolimit(1, (mx * 2 + 2)*VpBaseFig()));
mx = bv->Prec + cv->MaxPrec - 1;
if (mx <= av->Prec) mx = av->Prec + 1;
GUARD_OBJ(res, NewZeroWrapNolimit(1, mx * VpBaseFig()));
VpDivd(cv, res, av, bv);
VpSetPrecLimit(pl);
VpLeftRound(cv, VpGetRoundMode(), ix);
Expand Down Expand Up @@ -6164,7 +6165,7 @@ VpDivd(Real *c, Real *r, Real *a, Real *b)
word_c = c->MaxPrec;
word_r = r->MaxPrec;

if (word_a >= word_r) goto space_error;
if (word_a >= word_r || word_b + word_c - 2 >= word_r) goto space_error;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are two more goto space_error in this function.
One is on L6218

ind_r = ind_c + ind_b;
if (ind_r >= word_r) goto space_error;

And another in L6253

ind_r = b->Prec + ind_c - 1; // L6242
ind_r = b->Prec + ind_c; // L6250 (ind_c + 1 < word_c is ensured in L6247)
if (ind_r >= word_r) goto space_error;

Both has the same requirement: (word_b-1) + (word_c-1) < word_r


ind_r = 1;
r->frac[0] = 0;
Expand Down
32 changes: 31 additions & 1 deletion test/bigdecimal/test_bigdecimal.rb
Original file line number Diff line number Diff line change
Expand Up @@ -982,7 +982,7 @@ def test_div
def test_div_gh220
x = BigDecimal("1.0")
y = BigDecimal("3672577333.6608990499165058135986328125")
c = BigDecimal("0.272288343892592687909520102748926752911779209181321744700032723729015151607289998e-9")
c = BigDecimal("0.272288343892592687909520102748926752911779209181321745e-9")
assert_equal(c, x / y, "[GH-220]")
end

Expand All @@ -995,6 +995,36 @@ def test_div_precision
"(101/0.9163472602589686).precision >= (0.9163472602589686).precision #{bug13754}")
end

def test_div_various_precisions
a_precs = [5, 20, 70]
b_precs = [*5..80]
exponents = [0, 5]
a_precs.product(exponents, b_precs, exponents).each do |prec_a, ex_a, prec_b, ex_b|
a = BigDecimal('7.' + '1' * (prec_a - 1) + "e#{ex_a}")
b = BigDecimal('3.' + '1' * (prec_b - 1) + "e#{ex_b}")
c = a / b
max = [prec_a, prec_b, BigDecimal.double_fig].max
# Precision must be enough and not too large
precision_min = max + BigDecimal.double_fig / 2
precision_max = max + 2 * BigDecimal.double_fig
assert_includes(precision_min..precision_max, c.n_significant_digits)
end
end

def test_div2_various_precisions
a_precs = [5, 20, 70, 100]
b_precs = [5, 20, 70, 100]
c_precs = [5, 20, 30, 70, 100, 200]
exponents = [0, 5]
a_precs.product(exponents, b_precs, exponents, c_precs).each do |prec_a, ex_a, prec_b, ex_b, prec_c|
a = BigDecimal('7.' + '1' * (prec_a - 1) + "e#{ex_a}")
b = BigDecimal('3.' + '1' * (prec_b - 1) + "e#{ex_b}")
c = a.div(b, prec_c)
assert_in_delta(prec_c, c.n_significant_digits, 2)
assert_in_delta(a, c * b, a * 10**(1 - prec_c))
end
end

def test_div_with_float
assert_kind_of(BigDecimal, BigDecimal("3") / 1.5)
assert_equal(BigDecimal("0.5"), BigDecimal(1) / 2.0)
Expand Down