summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKenta Murata <mrkn@mrkn.jp>2021-11-25 14:55:29 +0900
committerKenta Murata <mrkn@mrkn.jp>2021-12-24 02:28:56 +0900
commitd905abb457d7319400f96d83a65de5eb304ab30d (patch)
treeb40fb7fea122417e72d149e609701847d3d84d64
parent38e98cbdb7c429d0042fd24efd9fec6516fb45f5 (diff)
[ruby/bigdecimal] Fix BigDecimal#precision for single DECDIG case
Fix GH-205 https://github.com/ruby/bigdecimal/commit/7d198394a2
-rw-r--r--ext/bigdecimal/bigdecimal.c22
-rw-r--r--test/bigdecimal/test_bigdecimal.rb16
2 files changed, 26 insertions, 12 deletions
diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c
index 668d9d1d4d..a8c0a48a2b 100644
--- a/ext/bigdecimal/bigdecimal.c
+++ b/ext/bigdecimal/bigdecimal.c
@@ -341,11 +341,19 @@ BigDecimal_precision(VALUE self)
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)
*/
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 784560d1d4..5a4108c603 100644
--- a/test/bigdecimal/test_bigdecimal.rb
+++ b/test/bigdecimal/test_bigdecimal.rb
@@ -2036,10 +2036,14 @@ class TestBigDecimal < Test::Unit::TestCase
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
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)