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

ruby-changes:64338

From: Kenta <ko1@a...>
Date: Sat, 19 Dec 2020 22:14:41 +0900 (JST)
Subject: [ruby-changes:64338] ff9e40811c (master): [bigdecimal] Add BigDecimal#precision

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

From ff9e40811c67b1090966b655f7adc0684fc58abe Mon Sep 17 00:00:00 2001
From: Kenta Murata <mrkn@m...>
Date: Sat, 19 Dec 2020 04:24:15 +0900
Subject: [bigdecimal] Add BigDecimal#precision

https://github.com/ruby/bigdecimal/commit/458eb66c49

diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c
index 039a4d7..ef975a0 100644
--- a/ext/bigdecimal/bigdecimal.c
+++ b/ext/bigdecimal/bigdecimal.c
@@ -378,6 +378,73 @@ BigDecimal_prec(VALUE self) https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/bigdecimal.c#L378
 }
 
 /*
+ * call-seq:
+ *   big_decimal.precision  ->  intreger
+ *
+ * Returns the number of decimal digits in this number.
+ *
+ * Example:
+ *
+ *   BigDecimal("0").precision  # => 0
+ *   BigDecimal("1").precision  # => 1
+ *   BigDecimal("-1e20").precision  # => 21
+ *   BigDecimal("1e-20").precision  # => 20
+ *   BigDecimal("Infinity").precision  # => 0
+ *   BigDecimal("-Infinity").precision  # => 0
+ *   BigDecimal("NaN").precision  # => 0
+ */
+static VALUE
+BigDecimal_precision(VALUE self)
+{
+    ENTER(1);
+
+    Real *p;
+    GUARD_OBJ(p, GetVpValue(self, 1));
+
+    /*
+     * 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.
+     * 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]
+     *    |------------------------> exponent == 3
+     */
+
+    ssize_t ex = p->exponent;
+    ssize_t precision;
+    if (ex < 0) {
+        precision = (-ex + 1) * BASE_FIG;  /* 1 is for p->frac[0] */
+        ex = 0;
+    }
+    else if (p->Prec > 0) {
+        BDIGIT x = p->frac[0];
+        for (precision = 0; x > 0; x /= 10) {
+            ++precision;
+        }
+    }
+
+    if (ex > (ssize_t)p->Prec) {
+        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;
+
+        precision += n * BASE_FIG;
+
+        if (ex < (ssize_t)p->Prec) {
+            BDIGIT x = p->frac[n];
+            for (; x > 0 && x % 10 == 0; x /= 10) {
+                --precision;
+            }
+        }
+    }
+
+    return SSIZET2NUM(precision);
+}
+
+/*
  * call-seq: hash
  *
  * Creates a hash for this BigDecimal.
@@ -3509,6 +3576,7 @@ Init_bigdecimal(void) https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/bigdecimal.c#L3576
 
     /* instance methods */
     rb_define_method(rb_cBigDecimal, "precs", BigDecimal_prec, 0);
+    rb_define_method(rb_cBigDecimal, "precision", BigDecimal_precision, 0);
 
     rb_define_method(rb_cBigDecimal, "add", BigDecimal_add2, 2);
     rb_define_method(rb_cBigDecimal, "sub", BigDecimal_sub2, 2);
diff --git a/test/bigdecimal/test_bigdecimal.rb b/test/bigdecimal/test_bigdecimal.rb
index a2af553..59726ad 100644
--- a/test/bigdecimal/test_bigdecimal.rb
+++ b/test/bigdecimal/test_bigdecimal.rb
@@ -1903,6 +1903,55 @@ class TestBigDecimal < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/bigdecimal/test_bigdecimal.rb#L1903
     EOS
   end
 
+  def test_precision_only_integer
+    assert_equal(0, BigDecimal(0).precision)
+    assert_equal(1, BigDecimal(1).precision)
+    assert_equal(1, BigDecimal(-1).precision)
+    assert_equal(2, BigDecimal(10).precision)
+    assert_equal(2, BigDecimal(-10).precision)
+    assert_equal(21, BigDecimal(100_000_000_000_000_000_000).precision)
+    assert_equal(21, BigDecimal(-100_000_000_000_000_000_000).precision)
+    assert_equal(103, BigDecimal("111e100").precision)
+    assert_equal(103, BigDecimal("-111e100").precision)
+  end
+
+  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.11").precision)
+    assert_equal(2, BigDecimal("-0.11").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)
+    assert_equal(100, BigDecimal("-111e-100").precision)
+  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(21, BigDecimal("100.000_000_000_000_000_001").precision)
+    assert_equal(21, BigDecimal("-100.000_000_000_000_000_001").precision)
+  end
+
+  def test_precision_special
+    BigDecimal.save_exception_mode do
+      BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false)
+      BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
+
+      assert_equal(0, BigDecimal("Infinity").precision)
+      assert_equal(0, BigDecimal("-Infinity").precision)
+      assert_equal(0, BigDecimal("NaN").precision)
+    end
+  end
+
   def test_initialize_copy_dup_clone_frozen_error
     bd = BigDecimal(1)
     bd2 = BigDecimal(2)
-- 
cgit v0.10.2


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

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