[ruby/bigdecimal] Fix BigDecimal#precision for single DECDIG case

Fix GH-205

https://github.com/ruby/bigdecimal/commit/7d198394a2
This commit is contained in:
Kenta Murata 2021-11-25 14:55:29 +09:00
parent 38e98cbdb7
commit d905abb457
No known key found for this signature in database
GPG Key ID: CEFE8AFB6081B062
2 changed files with 26 additions and 12 deletions

View File

@ -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;

View File

@ -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)