From d905abb457d7319400f96d83a65de5eb304ab30d Mon Sep 17 00:00:00 2001 From: Kenta Murata 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 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) -- cgit v1.2.3