ruby-changes:70485
From: Kenta <ko1@a...>
Date: Fri, 24 Dec 2021 02:30:26 +0900 (JST)
Subject: [ruby-changes:70485] 79712fc083 (master): [ruby/bigdecimal] Let BigDecimal#quo accept precision
https://git.ruby-lang.org/ruby.git/commit/?id=79712fc083 From 79712fc083f483b3ef174f6ab457f8b63b87c43e Mon Sep 17 00:00:00 2001 From: Kenta Murata <mrkn@m...> Date: Thu, 9 Dec 2021 22:24:12 +0900 Subject: [ruby/bigdecimal] Let BigDecimal#quo accept precision Fix GH-214. https://github.com/ruby/bigdecimal/commit/13e0e93f37 --- ext/bigdecimal/bigdecimal.c | 96 ++++++++++++++++++++++++++++---------- test/bigdecimal/test_bigdecimal.rb | 24 ++++++++++ 2 files changed, 96 insertions(+), 24 deletions(-) diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c index c648616b7a7..3be0ffec6df 100644 --- a/ext/bigdecimal/bigdecimal.c +++ b/ext/bigdecimal/bigdecimal.c @@ -115,6 +115,8 @@ static ID id_half; https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/bigdecimal.c#L115 */ static unsigned short VpGetException(void); static void VpSetException(unsigned short f); +static void VpCheckException(Real *p, bool always); +static VALUE VpCheckGetValue(Real *p); static void VpInternalRound(Real *c, size_t ixDigit, DECDIG vPrev, DECDIG v); static int VpLimitRound(Real *c, size_t ixDigit); static Real *VpCopy(Real *pv, Real const* const x); @@ -165,27 +167,6 @@ is_kind_of_BigDecimal(VALUE const v) https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/bigdecimal.c#L167 return rb_typeddata_is_kind_of(v, &BigDecimal_data_type); } -static void -VpCheckException(Real *p, bool always) -{ - if (VpIsNaN(p)) { - VpException(VP_EXCEPTION_NaN, "Computation results in 'NaN' (Not a Number)", always); - } - else if (VpIsPosInf(p)) { - VpException(VP_EXCEPTION_INFINITY, "Computation results in 'Infinity'", always); - } - else if (VpIsNegInf(p)) { - VpException(VP_EXCEPTION_INFINITY, "Computation results in '-Infinity'", always); - } -} - -static VALUE -VpCheckGetValue(Real *p) -{ - VpCheckException(p, false); - return p->obj; -} - NORETURN(static void cannot_be_coerced_into_BigDecimal(VALUE, VALUE)); static void @@ -1656,12 +1637,15 @@ BigDecimal_divide(VALUE self, VALUE r, Real **c, Real **res, Real **div) https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/bigdecimal.c#L1637 } /* call-seq: - * a / b -> bigdecimal - * quo(value) -> bigdecimal + * a / b -> bigdecimal * * Divide by the specified value. * + * The result precision will be the precision of the larger operand, + * but its minimum is 2*Float::DIG. + * * See BigDecimal#div. + * See BigDecimal#quo. */ static VALUE BigDecimal_div(VALUE self, VALUE r) @@ -1683,6 +1667,45 @@ BigDecimal_div(VALUE self, VALUE r) https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/bigdecimal.c#L1667 return VpCheckGetValue(c); } +static VALUE BigDecimal_round(int argc, VALUE *argv, VALUE self); + +/* call-seq: + * quo(value) -> bigdecimal + * quo(value, digits) -> bigdecimal + * + * Divide by the specified value. + * + * digits:: If specified and less than the number of significant digits of + * the result, the result is rounded to the given number of digits, + * according to the rounding mode indicated by BigDecimal.mode. + * + * If digits is 0 or omitted, the result is the same as for the + * / operator. + * + * See BigDecimal#/. + * See BigDecimal#div. + */ +static VALUE +BigDecimal_quo(int argc, VALUE *argv, VALUE self) +{ + VALUE value, digits, result; + SIGNED_VALUE n = -1; + + argc = rb_scan_args(argc, argv, "11", &value, &digits); + if (argc > 1) { + n = GetPrecisionInt(digits); + } + + if (n > 0) { + result = BigDecimal_div2(self, value, digits); + } + else { + result = BigDecimal_div(self, value); + } + + return result; +} + /* * %: mod = a%b = a - (a.to_f/b).floor * b * div = (a.to_f/b).floor @@ -1964,6 +1987,7 @@ BigDecimal_div2(VALUE self, VALUE b, VALUE n) https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/bigdecimal.c#L1987 * Document-method: BigDecimal#div * * call-seq: + * div(value) -> integer * div(value, digits) -> bigdecimal or integer * * Divide by the specified value. @@ -1978,6 +2002,9 @@ BigDecimal_div2(VALUE self, VALUE b, VALUE n) https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/bigdecimal.c#L2002 * If digits is not specified, the result is an integer, * by analogy with Float#div; see also BigDecimal#divmod. * + * See BigDecimal#/. + * See BigDecimal#quo. + * * Examples: * * a = BigDecimal("4") @@ -4272,7 +4299,7 @@ Init_bigdecimal(void) https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/bigdecimal.c#L4299 rb_define_method(rb_cBigDecimal, "-@", BigDecimal_neg, 0); rb_define_method(rb_cBigDecimal, "*", BigDecimal_mult, 1); rb_define_method(rb_cBigDecimal, "/", BigDecimal_div, 1); - rb_define_method(rb_cBigDecimal, "quo", BigDecimal_div, 1); + rb_define_method(rb_cBigDecimal, "quo", BigDecimal_quo, -1); rb_define_method(rb_cBigDecimal, "%", BigDecimal_mod, 1); rb_define_method(rb_cBigDecimal, "modulo", BigDecimal_mod, 1); rb_define_method(rb_cBigDecimal, "remainder", BigDecimal_remainder, 1); @@ -4446,6 +4473,27 @@ VpSetException(unsigned short f) https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/bigdecimal.c#L4473 bigdecimal_set_thread_local_exception_mode(f); } +static void +VpCheckException(Real *p, bool always) +{ + if (VpIsNaN(p)) { + VpException(VP_EXCEPTION_NaN, "Computation results in 'NaN' (Not a Number)", always); + } + else if (VpIsPosInf(p)) { + VpException(VP_EXCEPTION_INFINITY, "Computation results in 'Infinity'", always); + } + else if (VpIsNegInf(p)) { + VpException(VP_EXCEPTION_INFINITY, "Computation results in '-Infinity'", always); + } +} + +static VALUE +VpCheckGetValue(Real *p) +{ + VpCheckException(p, false); + return p->obj; +} + /* * Precision limit. */ diff --git a/test/bigdecimal/test_bigdecimal.rb b/test/bigdecimal/test_bigdecimal.rb index 9e8f0d593b5..bbfcbec4df6 100644 --- a/test/bigdecimal/test_bigdecimal.rb +++ b/test/bigdecimal/test_bigdecimal.rb @@ -1101,6 +1101,30 @@ class TestBigDecimal < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/bigdecimal/test_bigdecimal.rb#L1101 x.div(y, 100)) end + def test_quo_without_prec + x = BigDecimal(5) + y = BigDecimal(229) + assert_equal(BigDecimal("0.021834061135371179039301310043668122"), x.quo(y)) + end + + def test_quo_with_prec + begin + saved_mode = BigDecimal.mode(BigDecimal::ROUND_MODE) + BigDecimal.mode(BigDecimal::ROUND_MODE, :half_up) + + x = BigDecimal(5) + y = BigDecimal(229) + assert_equal(BigDecimal("0.021834061135371179039301310043668122"), x.quo(y, 0)) + assert_equal(BigDecimal("0.022"), x.quo(y, 2)) + assert_equal(BigDecimal("0.0218"), x.quo(y, 3)) + assert_equal(BigDecimal("0.0218341"), x.quo(y, 6)) + assert_equal(BigDecimal("0.02183406114"), x.quo(y, 10)) + assert_equal(BigDecimal("0.021834061135371179039301310043668122270742358078603"), x.quo(y, 50)) + ensure + BigDecimal.mode(BigDecimal::ROUND_MODE, saved_mode) + end + end + def test_abs_bigdecimal x = BigDecimal((2**100).to_s) assert_equal(1267650600228229401496703205376, x.abs) -- cgit v1.2.1 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/