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

ruby-changes:70472

From: Kenta <ko1@a...>
Date: Fri, 24 Dec 2021 02:30:04 +0900 (JST)
Subject: [ruby-changes:70472] d905abb457 (master): [ruby/bigdecimal] Fix BigDecimal#precision for single DECDIG case

https://git.ruby-lang.org/ruby.git/commit/?id=d905abb457

From d905abb457d7319400f96d83a65de5eb304ab30d Mon Sep 17 00:00:00 2001
From: Kenta Murata <mrkn@m...>
Date: Thu, 25 Nov 2021 14:55:29 +0900
Subject: [ruby/bigdecimal] Fix BigDecimal#precision for single DECDIG case

Fix GH-205

https://github.com/ruby/bigdecimal/commit/7d198394a2
---
 ext/bigdecimal/bigdecimal.c        | 22 ++++++++++++++++++----
 test/bigdecimal/test_bigdecimal.rb | 16 ++++++++--------
 2 files changed, 26 insertions(+), 12 deletions(-)

diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c
index 668d9d1d4d3..a8c0a48a2b6 100644
--- a/ext/bigdecimal/bigdecimal.c
+++ b/ext/bigdecimal/bigdecimal.c
@@ -341,11 +341,19 @@ BigDecimal_precision(VALUE self) https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/bigdecimal.c#L341
 
     Real *p;
     GUARD_OBJ(p, GetVpValue(self, 1));
+    if (VpIsZero(p) || !VpIsDef(p)) return INT2FIX(0);
 
     /*
      * The most significant digit is frac[0], and the least significant digit is frac[Prec-1].
      * When the exponent is zero, the decimal point is located just before frac[0].
+     *
+     *
      * When the exponent is negative, the decimal point moves to leftward.
+     * In this case, the precision can be calculated by BASE_FIG * (-exponent + Prec) - ntz.
+     *
+     *   0 . 0000 0000 | frac[0] frac[1] ... frac[Prec-1]
+     *      <----------| exponent == -2
+     *
      * Conversely, when the exponent is positive, the decimal point moves to rightward.
      *
      *    | frac[0] frac[1] frac[2] . frac[3] frac[4] ... frac[Prec-1]
@@ -353,24 +361,30 @@ BigDecimal_precision(VALUE self) https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/bigdecimal.c#L361
      */
 
     ssize_t ex = p->exponent;
-    ssize_t precision = 0;
+
+    /* Count the number of decimal digits before frac[1]. */
+    ssize_t precision = BASE_FIG;  /* The number of decimal digits in frac[0]. */
     if (ex < 0) {
-        precision = (-ex + 1) * BASE_FIG;  /* 1 is for p->frac[0] */
+        precision += -ex * BASE_FIG;  /* The number of leading zeros before frac[0]. */
         ex = 0;
     }
-    else if (p->Prec > 0) {
+    else if (ex > 0) {
+        /* Count the number of decimal digits without the leading zeros in
+         * the most significant digit in the integral part. */
         DECDIG x = p->frac[0];
         for (precision = 0; x > 0; x /= 10) {
             ++precision;
         }
     }
 
+    /* Count the number of decimal digits after frac[0]. */
     if (ex > (ssize_t)p->Prec) {
+        /* In this case the number is an integer with multiple trailing zeros. */
         precision += (ex - 1) * BASE_FIG;
     }
     else if (p->Prec > 0) {
         ssize_t n = (ssize_t)p->Prec - 1;
-        while (n > 0 && p->frac[n] == 0) --n;
+        while (n > 0 && p->frac[n] == 0) --n;  /* Skip trailing zeros, just in case. */
 
         precision += n * BASE_FIG;
 
diff --git a/test/bigdecimal/test_bigdecimal.rb b/test/bigdecimal/test_bigdecimal.rb
index 784560d1d49..5a4108c6037 100644
--- a/test/bigdecimal/test_bigdecimal.rb
+++ b/test/bigdecimal/test_bigdecimal.rb
@@ -2036,10 +2036,14 @@ class TestBigDecimal < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/bigdecimal/test_bigdecimal.rb#L2036
   def test_precision_only_fraction
     assert_equal(1, BigDecimal("0.1").precision)
     assert_equal(1, BigDecimal("-0.1").precision)
-    assert_equal(1, BigDecimal("0.01").precision)
-    assert_equal(1, BigDecimal("-0.01").precision)
+    assert_equal(2, BigDecimal("0.01").precision)
+    assert_equal(2, BigDecimal("-0.01").precision)
     assert_equal(2, BigDecimal("0.11").precision)
     assert_equal(2, BigDecimal("-0.11").precision)
+    assert_equal(9, BigDecimal("0.000_000_001").precision)
+    assert_equal(9, BigDecimal("-0.000_000_001").precision)
+    assert_equal(10, BigDecimal("0.000_000_000_1").precision)
+    assert_equal(10, BigDecimal("-0.000_000_000_1").precision)
     assert_equal(21, BigDecimal("0.000_000_000_000_000_000_001").precision)
     assert_equal(21, BigDecimal("-0.000_000_000_000_000_000_001").precision)
     assert_equal(100, BigDecimal("111e-100").precision)
@@ -2047,12 +2051,8 @@ class TestBigDecimal < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/bigdecimal/test_bigdecimal.rb#L2051
   end
 
   def test_precision_full
-    assert_equal(1, BigDecimal("0.1").precision)
-    assert_equal(1, BigDecimal("-0.1").precision)
-    assert_equal(1, BigDecimal("0.01").precision)
-    assert_equal(1, BigDecimal("-0.01").precision)
-    assert_equal(2, BigDecimal("0.11").precision)
-    assert_equal(2, BigDecimal("-0.11").precision)
+    assert_equal(5, BigDecimal("11111e-2").precision)
+    assert_equal(5, BigDecimal("-11111e-2").precision)
     assert_equal(5, BigDecimal("11111e-2").precision)
     assert_equal(5, BigDecimal("-11111e-2").precision)
     assert_equal(21, BigDecimal("100.000_000_000_000_000_001").precision)
-- 
cgit v1.2.1


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

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