Skip to content

Integrate BigDecimal_div and BigDecimal_div2 #329

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 29, 2025

Conversation

tompng
Copy link
Member

@tompng tompng commented May 27, 2025

Fixes #220, #222 and #272

BigDecimal_div and BigDecimal_div2 both have rounding problem (#272 and #328)
Integrating two division logic will make it easy to fix them.

Precision calculation for a / b and a.div(b, 0) is improved. There is no exponential precision growth anymore.

tompng added 2 commits May 27, 2025 22:57
…ecified

Fixes exponential precision grouth.
`max(divisor_prec, dividend_prec) + extra_prec` is enough for division precision.
GUARD_OBJ(cv, NewZeroWrapLimited(1, ix + 3 * VpBaseFig()));

mx = bv->Prec + cv->MaxPrec - 1;
if (mx <= av->Prec) mx = av->Prec + 1;
Copy link
Member Author

Choose a reason for hiding this comment

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

Correct allocation size calculation of res.
VpDivd requires word_a < word_r && word_b + word_c - 2 < word_r

ssize_t a_prec, b_prec;
BigDecimal_count_precision_and_scale(av->obj, &a_prec, NULL);
BigDecimal_count_precision_and_scale(bv->obj, &b_prec, NULL);
ix = ((a_prec > b_prec) ? a_prec : b_prec) + BIGDECIMAL_DOUBLE_FIGURES;
Copy link
Member Author

Choose a reason for hiding this comment

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

max(a_prec, b_prec) + extra_prec is enough. Fixes exponential precision growth.

@@ -6164,7 +6119,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
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

@@ -510,10 +510,10 @@ def test_round_up

BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_HALF_DOWN)
assert_operator(n4, :>, n4 / 9 * 9)
assert_operator(n5, :>, n5 / 9 * 9)
assert_operator(n5, :<, n5 / 9 * 9)
Copy link
Member Author

@tompng tompng May 27, 2025

Choose a reason for hiding this comment

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

Half down rules are:

123.5 → 123 (half down)
123.50001 → 124 (round up because it's larger than half)
0.55550 → 0.555
0.55551 → 0.556
0.55555555.. → 0.556 (Because it's larger than half)

In master branch, rounding is inconsistent between a / b and a.div(b, prec).

BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_HALF_DOWN)

BigDecimal(5)/9
#=> 0.555555555555555555555555555555555555e0 (ends with 5)
BigDecimal(5).div(9, 36)
#=> 0.555555555555555555555555555555555556e0 (ends with 6)

@tompng tompng mentioned this pull request May 27, 2025
@mrkn mrkn mentioned this pull request May 29, 2025
@mrkn mrkn merged commit 1fa3eff into ruby:master May 29, 2025
78 checks passed
@tompng tompng deleted the div_div2_integrate branch May 29, 2025 09:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

"ERROR(VpDivd): space for remainder too small" for certain division
2 participants