From a32f6cb9e23ee04b56bf9b9257c60fabbeeceb2e Mon Sep 17 00:00:00 2001 From: Kenta Murata Date: Fri, 24 Dec 2021 00:26:34 +0900 Subject: [PATCH] Fix the result precision of BigDecimal#divmod --- ext/bigdecimal/bigdecimal.c | 21 +++++++++++++++++---- test/bigdecimal/test_bigdecimal.rb | 6 ++++-- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c index 5af6c7c5..4111fbd7 100644 --- a/ext/bigdecimal/bigdecimal.c +++ b/ext/bigdecimal/bigdecimal.c @@ -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); @@ -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 * @@ -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); @@ -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; diff --git a/test/bigdecimal/test_bigdecimal.rb b/test/bigdecimal/test_bigdecimal.rb index bbfcbec4..825d7ec9 100644 --- a/test/bigdecimal/test_bigdecimal.rb +++ b/test/bigdecimal/test_bigdecimal.rb @@ -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