From cd3f7897ff44deea673686dda9dc0758417e3d7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gre=CC=81gory=20Lureau?= Date: Mon, 10 Jun 2024 19:00:44 +0200 Subject: [PATCH] Improve performances of BigDecimal.numberOfDecimalDigits(). --- .../kotlin/bignum/decimal/BigDecimal.kt | 14 ++++++++++++- .../bignum/decimal/BigDecimalRoundingTests.kt | 21 ++++++++++--------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/decimal/BigDecimal.kt b/bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/decimal/BigDecimal.kt index add0f441..2e803a7f 100644 --- a/bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/decimal/BigDecimal.kt +++ b/bignum/src/commonMain/kotlin/com/ionspin/kotlin/bignum/decimal/BigDecimal.kt @@ -1484,6 +1484,7 @@ class BigDecimal private constructor( * 12.345 will return 5 * 0.001 will return 4 * 123000 will return 6 + * 0.010000 will return 3 (trailing zeros are not counted) */ override fun numberOfDecimalDigits(): Long { val numberOfDigits = when { @@ -1491,7 +1492,18 @@ class BigDecimal private constructor( exponent > 0 && exponent > precision -> exponent + 1 // Significand is already 10^1 when exponent is > 0 exponent > 0 && exponent == precision -> precision + 1 // Same as above exponent < 0 -> exponent.absoluteValue + precision - exponent == 0L -> removeTrailingZeroes(this).precision + exponent == 0L -> { + var s = significand + if (s.isZero()) return 1L + var zeroCount = -1L + do { + val res = s.divrem(BigInteger.TEN) + s = res.quotient + zeroCount++ + } while (res.remainder.isZero()) + precision - zeroCount + } + else -> throw RuntimeException("Invalid case when getting number of decimal digits") } return numberOfDigits diff --git a/bignum/src/commonTest/kotlin/com/ionspin/kotlin/bignum/decimal/BigDecimalRoundingTests.kt b/bignum/src/commonTest/kotlin/com/ionspin/kotlin/bignum/decimal/BigDecimalRoundingTests.kt index 6ad2b039..64526df7 100644 --- a/bignum/src/commonTest/kotlin/com/ionspin/kotlin/bignum/decimal/BigDecimalRoundingTests.kt +++ b/bignum/src/commonTest/kotlin/com/ionspin/kotlin/bignum/decimal/BigDecimalRoundingTests.kt @@ -31,17 +31,18 @@ class BigDecimalRoundingTests { @Test fun testNumberOfDigits() { - val a = BigDecimal.fromIntWithExponent(123, 3) - val b = BigDecimal.fromIntWithExponent(1, -3) - val c = BigDecimal.fromIntWithExponent(12345, 3) - val d = BigDecimal.fromIntAsSignificand(10000) - assertTrue { - a.numberOfDecimalDigits() == 4L && - b.numberOfDecimalDigits() == 4L && - c.numberOfDecimalDigits() == 5L && - d.numberOfDecimalDigits() == 1L + assertEquals(4, BigDecimal.parseString("1230").numberOfDecimalDigits()) + assertEquals(4, BigDecimal.parseString("0.001").numberOfDecimalDigits()) + assertEquals(5, BigDecimal.parseString("1234.5").numberOfDecimalDigits()) + assertEquals(1, BigDecimal.parseString("1.0000").numberOfDecimalDigits()) + assertEquals(5, BigDecimal.parseString("1.0001000").numberOfDecimalDigits()) + assertEquals(6, BigDecimal.parseString("10.0001000").numberOfDecimalDigits()) + assertEquals(1, BigDecimal.parseString("0").numberOfDecimalDigits()) + assertEquals(1, BigDecimal.parseString("0.0").numberOfDecimalDigits()) + assertEquals(1, BigDecimal.parseString("00.00").numberOfDecimalDigits()) + assertEquals(1, BigDecimal.parseString("1").numberOfDecimalDigits()) + assertEquals(2, BigDecimal.parseString("10").numberOfDecimalDigits()) } - } @Test fun testRoundSignificand() {