Improve Decimal64.toString() implementation 35/101635/22
authorOleksandrZharov <Oleksandr.Zharov@pantheon.tech>
Tue, 28 Jun 2022 14:56:08 +0000 (16:56 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Fri, 29 Jul 2022 20:28:21 +0000 (22:28 +0200)
Change implementation of Decimal64.toString() method to:
pad unscaled value to scale + 1 size string, insert '.',
and remove trailing zeros.

JIRA: YANGTOOLS-1439
Change-Id: Ifb21ce4cdcf17d91da669c8f2c5e73de6a3edf22
Signed-off-by: OleksandrZharov <Oleksandr.Zharov@pantheon.tech>
Signed-off-by: Ivan Hrasko <ivan.hrasko@pantheon.tech>
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
common/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/Decimal64.java
common/yang-common/src/test/java/org/opendaylight/yangtools/yang/common/Decimal64Test.java

index 0b4c4e9f3adf6cab57c8aff78e7b791c4a158d78..d5484124bce5d534c6445951043c62777a22461e 100644 (file)
@@ -13,7 +13,6 @@ import static java.util.Objects.requireNonNull;
 
 import com.google.common.annotations.Beta;
 import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Strings;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.util.Optional;
@@ -561,22 +560,31 @@ public class Decimal64 extends Number implements CanonicalValue<Decimal64> {
         // before and after the decimal point.  The value zero is represented as
         // "0.0".
 
-        final long intPart = intPart();
-        final long fracPart = fracPart();
-        final StringBuilder sb = new StringBuilder(21);
-        if (intPart == 0 && fracPart < 0) {
-            sb.append('-');
+        // Pad unscaled value to scale + 1 size string starting after optional '-' sign
+        final var builder = new StringBuilder(21).append(value);
+        final int start = value < 0 ? 1 : 0;
+        final int scale = scale();
+        final int padding = scale + 1 + start - builder.length();
+        if (padding > 0) {
+            builder.insert(start, "0".repeat(padding));
         }
-        sb.append(intPart).append('.');
 
-        if (fracPart != 0) {
-            // We may need to zero-pad the fraction part
-            sb.append(Strings.padStart(Long.toString(Math.abs(fracPart)), scale(), '0'));
-        } else {
-            sb.append('0');
+        // The first digit of the fraction part is now 'scale' from the end. We will insert the decimal point there,
+        // but also we it is the digit we never trim.
+        final int length = builder.length();
+        final int firstDecimal = length - scale;
+
+        // Remove trailing '0's from decimal part. We walk backwards from the last character stop at firstDecimal
+        int significantLength = length;
+        for (int i = length - 1; i > firstDecimal && builder.charAt(i) == '0'; --i) {
+            significantLength = i;
+        }
+        if (significantLength != length) {
+            builder.setLength(significantLength);
         }
 
-        return sb.toString();
+        // Insert '.' before the first decimal and we're done
+        return builder.insert(firstDecimal, '.').toString();
     }
 
     @Override
index 9aae74a276e6144ce5907c3c91d8e1e825979caf..9b38a85c677e6e44488752aa709c7243643a594e 100644 (file)
@@ -228,6 +228,24 @@ public class Decimal64Test {
         assertEquals("-0.63", Decimal64.valueOf("-0.63").toString());
     }
 
+    @Test
+    public void testFractionPartToString() {
+        assertEquals("0.3", Decimal64.valueOf("0.3").toString());
+        assertEquals("0.03", Decimal64.valueOf("0.03").toString());
+        assertEquals("0.003", Decimal64.valueOf("0.003").toString());
+        assertEquals("-0.3", Decimal64.valueOf("-0.3").toString());
+        assertEquals("-0.03", Decimal64.valueOf("-0.03").toString());
+        assertEquals("-0.003", Decimal64.valueOf("-0.003").toString());
+    }
+
+    @Test
+    public void testScalingToString() {
+        assertEquals("30.0", Decimal64.of(1, 300).toString());
+        assertEquals("3.0", Decimal64.of(2, 300).toString());
+        assertEquals("0.3", Decimal64.of(3, 300).toString());
+        assertEquals("0.03", Decimal64.of(4, 300).toString());
+    }
+
     @Test
     public void testBoundaries() {
         assertEquals(-128L, Decimal64.valueOf(1, Byte.MIN_VALUE).longValue());