[前][次][番号順一覧][スレッド一覧]

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/

[前][次][番号順一覧][スレッド一覧]