Skip to content

Commit 7d19839

Browse files
committed
Fix BigDecimal#precision for single DECDIG case
Fix GH-205
1 parent 252748d commit 7d19839

File tree

2 files changed

+26
-12
lines changed

2 files changed

+26
-12
lines changed

ext/bigdecimal/bigdecimal.c

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -341,36 +341,50 @@ BigDecimal_precision(VALUE self)
341341

342342
Real *p;
343343
GUARD_OBJ(p, GetVpValue(self, 1));
344+
if (VpIsZero(p) || !VpIsDef(p)) return INT2FIX(0);
344345

345346
/*
346347
* The most significant digit is frac[0], and the least significant digit is frac[Prec-1].
347348
* When the exponent is zero, the decimal point is located just before frac[0].
349+
*
350+
*
348351
* When the exponent is negative, the decimal point moves to leftward.
352+
* In this case, the precision can be calculated by BASE_FIG * (-exponent + Prec) - ntz.
353+
*
354+
* 0 . 0000 0000 | frac[0] frac[1] ... frac[Prec-1]
355+
* <----------| exponent == -2
356+
*
349357
* Conversely, when the exponent is positive, the decimal point moves to rightward.
350358
*
351359
* | frac[0] frac[1] frac[2] . frac[3] frac[4] ... frac[Prec-1]
352360
* |------------------------> exponent == 3
353361
*/
354362

355363
ssize_t ex = p->exponent;
356-
ssize_t precision = 0;
364+
365+
/* Count the number of decimal digits before frac[1]. */
366+
ssize_t precision = BASE_FIG; /* The number of decimal digits in frac[0]. */
357367
if (ex < 0) {
358-
precision = (-ex + 1) * BASE_FIG; /* 1 is for p->frac[0] */
368+
precision += -ex * BASE_FIG; /* The number of leading zeros before frac[0]. */
359369
ex = 0;
360370
}
361-
else if (p->Prec > 0) {
371+
else if (ex > 0) {
372+
/* Count the number of decimal digits without the leading zeros in
373+
* the most significant digit in the integral part. */
362374
DECDIG x = p->frac[0];
363375
for (precision = 0; x > 0; x /= 10) {
364376
++precision;
365377
}
366378
}
367379

380+
/* Count the number of decimal digits after frac[0]. */
368381
if (ex > (ssize_t)p->Prec) {
382+
/* In this case the number is an integer with multiple trailing zeros. */
369383
precision += (ex - 1) * BASE_FIG;
370384
}
371385
else if (p->Prec > 0) {
372386
ssize_t n = (ssize_t)p->Prec - 1;
373-
while (n > 0 && p->frac[n] == 0) --n;
387+
while (n > 0 && p->frac[n] == 0) --n; /* Skip trailing zeros, just in case. */
374388

375389
precision += n * BASE_FIG;
376390

test/bigdecimal/test_bigdecimal.rb

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2036,23 +2036,23 @@ def test_precision_only_integer
20362036
def test_precision_only_fraction
20372037
assert_equal(1, BigDecimal("0.1").precision)
20382038
assert_equal(1, BigDecimal("-0.1").precision)
2039-
assert_equal(1, BigDecimal("0.01").precision)
2040-
assert_equal(1, BigDecimal("-0.01").precision)
2039+
assert_equal(2, BigDecimal("0.01").precision)
2040+
assert_equal(2, BigDecimal("-0.01").precision)
20412041
assert_equal(2, BigDecimal("0.11").precision)
20422042
assert_equal(2, BigDecimal("-0.11").precision)
2043+
assert_equal(9, BigDecimal("0.000_000_001").precision)
2044+
assert_equal(9, BigDecimal("-0.000_000_001").precision)
2045+
assert_equal(10, BigDecimal("0.000_000_000_1").precision)
2046+
assert_equal(10, BigDecimal("-0.000_000_000_1").precision)
20432047
assert_equal(21, BigDecimal("0.000_000_000_000_000_000_001").precision)
20442048
assert_equal(21, BigDecimal("-0.000_000_000_000_000_000_001").precision)
20452049
assert_equal(100, BigDecimal("111e-100").precision)
20462050
assert_equal(100, BigDecimal("-111e-100").precision)
20472051
end
20482052

20492053
def test_precision_full
2050-
assert_equal(1, BigDecimal("0.1").precision)
2051-
assert_equal(1, BigDecimal("-0.1").precision)
2052-
assert_equal(1, BigDecimal("0.01").precision)
2053-
assert_equal(1, BigDecimal("-0.01").precision)
2054-
assert_equal(2, BigDecimal("0.11").precision)
2055-
assert_equal(2, BigDecimal("-0.11").precision)
2054+
assert_equal(5, BigDecimal("11111e-2").precision)
2055+
assert_equal(5, BigDecimal("-11111e-2").precision)
20562056
assert_equal(5, BigDecimal("11111e-2").precision)
20572057
assert_equal(5, BigDecimal("-11111e-2").precision)
20582058
assert_equal(21, BigDecimal("100.000_000_000_000_000_001").precision)

0 commit comments

Comments
 (0)