This is an automated email from the ASF dual-hosted git repository.

mattjuntunen pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-geometry.git


The following commit(s) were added to refs/heads/master by this push:
     new b9ff14e  improvements to DoubleFormats performance
b9ff14e is described below

commit b9ff14e03b06f5b4ca1881ec905d64926ba09f21
Author: Matt Juntunen <mattjuntu...@apache.org>
AuthorDate: Fri Jun 11 22:08:41 2021 -0400

    improvements to DoubleFormats performance
---
 commons-geometry-examples/examples-jmh/pom.xml     |   5 +
 .../geometry/examples/jmh/BenchmarkUtils.java      |  43 ++++-
 .../AffineTransformMatrixPerformance.java          |   6 +-
 .../jmh/io/core/DoubleFormatsPerformance.java      | 183 +++++++++++++++++++++
 .../examples/jmh/io/core/package-info.java         |  23 +++
 .../geometry/io/core/utils/ParsedDouble.java       | 137 ++++++++-------
 6 files changed, 323 insertions(+), 74 deletions(-)

diff --git a/commons-geometry-examples/examples-jmh/pom.xml 
b/commons-geometry-examples/examples-jmh/pom.xml
index 0165312..7527390 100644
--- a/commons-geometry-examples/examples-jmh/pom.xml
+++ b/commons-geometry-examples/examples-jmh/pom.xml
@@ -58,6 +58,11 @@
 
     <dependency>
       <groupId>org.apache.commons</groupId>
+      <artifactId>commons-geometry-io-core</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.commons</groupId>
       <artifactId>commons-rng-simple</artifactId>
     </dependency>
 
diff --git 
a/commons-geometry-examples/examples-jmh/src/main/java/org/apache/commons/geometry/examples/jmh/BenchmarkUtils.java
 
b/commons-geometry-examples/examples-jmh/src/main/java/org/apache/commons/geometry/examples/jmh/BenchmarkUtils.java
index a6e7583..526a1f8 100644
--- 
a/commons-geometry-examples/examples-jmh/src/main/java/org/apache/commons/geometry/examples/jmh/BenchmarkUtils.java
+++ 
b/commons-geometry-examples/examples-jmh/src/main/java/org/apache/commons/geometry/examples/jmh/BenchmarkUtils.java
@@ -22,22 +22,36 @@ import org.apache.commons.rng.UniformRandomProvider;
  */
 public final class BenchmarkUtils {
 
+    /** Default min exponent for random double values. */
+    public static final int DEFAULT_MIN_EXP = -64;
+
+    /** Default max exponent for random double values. */
+    public static final int DEFAULT_MAX_EXP = 64;
+
     /** Utility class; no instantiation. */
     private BenchmarkUtils() {}
 
-    /** Creates a random double number with a random sign and mantissa and a 
large range for
-     * the exponent. The numbers will not be uniform over the range.
+    /** Creates a random double number with a random sign and mantissa and a 
large, default
+     * range for the exponent. The numbers will not be uniform over the range.
      * @param rng random number generator
      * @return the random number
      */
     public static double randomDouble(final UniformRandomProvider rng) {
+        return randomDouble(DEFAULT_MIN_EXP, DEFAULT_MAX_EXP, rng);
+    }
+
+    /** Create a random double value with exponent in the range {@code 
[minExp, maxExp]}.
+     * @param minExp minimum exponent; must be less than {@code maxExp}
+     * @param maxExp maximum exponent; must be greater than {@code minExp}
+     * @param rng random number generator
+     * @return random double
+     */
+    public static double randomDouble(final int minExp, final int maxExp, 
final UniformRandomProvider rng) {
         // Create random doubles using random bits in the sign bit and the 
mantissa.
-        // Then create an exponent in the range -64 to 64. Thus the sum product
-        // of 4 max or min values will not over or underflow.
         final long mask = ((1L << 52) - 1) | 1L << 63;
         final long bits = rng.nextLong() & mask;
         // The exponent must be unsigned so + 1023 to the signed exponent
-        final long exp = rng.nextInt(129) - 64 + 1023;
+        final long exp = rng.nextInt(maxExp - minExp + 1) + minExp + 1023;
         return Double.longBitsToDouble(bits | (exp << 52));
     }
 
@@ -46,13 +60,24 @@ public final class BenchmarkUtils {
      * @param len array length
      * @return array containing {@code len} random doubles
      */
-    public static double[] randomDoubleArray(final UniformRandomProvider rng, 
final int len) {
-        final double[] arr = new double[len];
+    public static double[] randomDoubleArray(final int len, final 
UniformRandomProvider rng) {
+        return randomDoubleArray(len, DEFAULT_MIN_EXP, DEFAULT_MAX_EXP, rng);
+    }
 
+    /** Create an array with the given length containing random doubles with 
exponents in the range
+     * {@code [minExp, maxExp]}.
+     * @param len array length
+     * @param minExp minimum exponent; must be less than {@code maxExp}
+     * @param maxExp maximum exponent; must be greater than {@code minExp}
+     * @param rng random number generator
+     * @return array of random doubles
+     */
+    public static double[] randomDoubleArray(final int len, final int minExp, 
final int maxExp,
+            final UniformRandomProvider rng) {
+        final double[] arr = new double[len];
         for (int i = 0; i < arr.length; ++i) {
-            arr[i] = randomDouble(rng);
+            arr[i] = randomDouble(minExp, maxExp, rng);
         }
-
         return arr;
     }
 }
diff --git 
a/commons-geometry-examples/examples-jmh/src/main/java/org/apache/commons/geometry/examples/jmh/euclidean/AffineTransformMatrixPerformance.java
 
b/commons-geometry-examples/examples-jmh/src/main/java/org/apache/commons/geometry/examples/jmh/euclidean/AffineTransformMatrixPerformance.java
index f05d321..d437f4b 100644
--- 
a/commons-geometry-examples/examples-jmh/src/main/java/org/apache/commons/geometry/examples/jmh/euclidean/AffineTransformMatrixPerformance.java
+++ 
b/commons-geometry-examples/examples-jmh/src/main/java/org/apache/commons/geometry/examples/jmh/euclidean/AffineTransformMatrixPerformance.java
@@ -111,7 +111,7 @@ public class AffineTransformMatrixPerformance {
         public void setup() {
             final UniformRandomProvider rand = 
RandomSource.create(RandomSource.XO_RO_SHI_RO_128_PP);
 
-            transform = 
AffineTransformMatrix1D.of(BenchmarkUtils.randomDoubleArray(rand, 2));
+            transform = 
AffineTransformMatrix1D.of(BenchmarkUtils.randomDoubleArray(2, rand));
         }
     }
 
@@ -135,7 +135,7 @@ public class AffineTransformMatrixPerformance {
         public void setup() {
             final UniformRandomProvider rand = 
RandomSource.create(RandomSource.XO_RO_SHI_RO_128_PP);
 
-            transform = 
AffineTransformMatrix2D.of(BenchmarkUtils.randomDoubleArray(rand, 6));
+            transform = 
AffineTransformMatrix2D.of(BenchmarkUtils.randomDoubleArray(6, rand));
         }
     }
 
@@ -159,7 +159,7 @@ public class AffineTransformMatrixPerformance {
         public void setup() {
             final UniformRandomProvider rand = 
RandomSource.create(RandomSource.XO_RO_SHI_RO_128_PP);
 
-            transform = 
AffineTransformMatrix3D.of(BenchmarkUtils.randomDoubleArray(rand, 12));
+            transform = 
AffineTransformMatrix3D.of(BenchmarkUtils.randomDoubleArray(12, rand));
         }
     }
 
diff --git 
a/commons-geometry-examples/examples-jmh/src/main/java/org/apache/commons/geometry/examples/jmh/io/core/DoubleFormatsPerformance.java
 
b/commons-geometry-examples/examples-jmh/src/main/java/org/apache/commons/geometry/examples/jmh/io/core/DoubleFormatsPerformance.java
new file mode 100644
index 0000000..dc83014
--- /dev/null
+++ 
b/commons-geometry-examples/examples-jmh/src/main/java/org/apache/commons/geometry/examples/jmh/io/core/DoubleFormatsPerformance.java
@@ -0,0 +1,183 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.geometry.examples.jmh.io.core;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.text.DecimalFormat;
+import java.util.concurrent.TimeUnit;
+import java.util.function.DoubleFunction;
+
+import org.apache.commons.geometry.examples.jmh.BenchmarkUtils;
+import org.apache.commons.geometry.io.core.utils.DoubleFormats;
+import org.apache.commons.rng.simple.RandomSource;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Level;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+import org.openjdk.jmh.infra.Blackhole;
+
+/** Benchmarks for the {@link DoubleFormats} class.
+ */
+@BenchmarkMode(Mode.AverageTime)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
+@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
+@Fork(value = 1, jvmArgs = {"-server", "-Xms512M", "-Xmx512M"})
+public class DoubleFormatsPerformance {
+
+    /** Benchmark input providing a source of random double values. */
+    @State(Scope.Thread)
+    public static class DoubleInput {
+
+        /** The number of doubles in the input array. */
+        @Param({"10000"})
+        private int size;
+
+        /** Minimum base 2 exponent for random input doubles. */
+        @Param("-20")
+        private int minExp;
+
+        /** Maximum base 2 exponent for random input doubles. */
+        @Param("20")
+        private int maxExp;
+
+        /** Double input array. */
+        private double[] input;
+
+        /** Get the input doubles.
+         * @return the input doubles
+         */
+        public double[] getInput() {
+            return input;
+        }
+
+        /** Set up the instance for the benchmark. */
+        @Setup(Level.Iteration)
+        public void setup() {
+            input = BenchmarkUtils.randomDoubleArray(size, minExp, maxExp,
+                    RandomSource.create(RandomSource.XO_RO_SHI_RO_128_PP));
+        }
+    }
+
+    /** Run a benchmark test on a function accepting a double argument.
+     * @param <T> function output type
+     * @param input double array
+     * @param bh jmh blackhole for consuming output
+     * @param fn function to call
+     */
+    private static <T> void runDoubleFunction(final DoubleInput input, final 
Blackhole bh,
+            final DoubleFunction<T> fn) {
+        for (final double d : input.getInput()) {
+            bh.consume(fn.apply(d));
+        }
+    }
+
+    /** Benchmark testing just the overhead of the benchmark harness.
+     * @param input benchmark state input
+     * @param bh jmh blackhole for consuming output
+     */
+    @Benchmark
+    public void baseline(final DoubleInput input, final Blackhole bh) {
+        runDoubleFunction(input, bh, d -> "");
+    }
+
+    /** Benchmark testing the {@link Double#toString()} method.
+     * @param input benchmark state input
+     * @param bh jmh blackhole for consuming output
+     */
+    @Benchmark
+    public void doubleToString(final DoubleInput input, final Blackhole bh) {
+        runDoubleFunction(input, bh, Double::toString);
+    }
+
+    /** Benchmark testing the {@link String#format(String, Object...)} method.
+     * @param input benchmark state input
+     * @param bh jmh blackhole for consuming output
+     */
+    @Benchmark
+    public void stringFormat(final DoubleInput input, final Blackhole bh) {
+        runDoubleFunction(input, bh, d -> String.format("%d", d));
+    }
+
+    /** Benchmark testing the BigDecimal formatting performance.
+     * @param input benchmark state input
+     * @param bh jmh blackhole for consuming output
+     */
+    @Benchmark
+    public void bigDecimal(final DoubleInput input, final Blackhole bh) {
+        final DoubleFunction<String> fn = d -> BigDecimal.valueOf(d)
+                .setScale(3, RoundingMode.HALF_EVEN)
+                .stripTrailingZeros()
+                .toString();
+        runDoubleFunction(input, bh, fn);
+    }
+
+    /** Benchmark testing the {@link DecimalFormat} class.
+     * @param input benchmark state input
+     * @param bh jmh blackhole for consuming output
+     */
+    @Benchmark
+    public void decimalFormat(final DoubleInput input, final Blackhole bh) {
+        final DecimalFormat fmt = new DecimalFormat("0.###");
+        runDoubleFunction(input, bh, fmt::format);
+    }
+
+    /** Benchmark testing the {@link DoubleFormats#createDefault(int, int)} 
method.
+     * @param input benchmark state input
+     * @param bh jmh blackhole for consuming output
+     */
+    @Benchmark
+    public void doubleFormatsDefault(final DoubleInput input, final Blackhole 
bh) {
+        runDoubleFunction(input, bh, DoubleFormats.createDefault(0, -3));
+    }
+
+    /** Benchmark testing the {@link DoubleFormats#createPlain(int, int)} 
method.
+     * @param input benchmark state input
+     * @param bh jmh blackhole for consuming output
+     */
+    @Benchmark
+    public void doubleFormatsPlain(final DoubleInput input, final Blackhole 
bh) {
+        runDoubleFunction(input, bh, DoubleFormats.createPlain(0, -3));
+    }
+
+    /** Benchmark testing the {@link DoubleFormats#createScientific(int, int)} 
method.
+     * @param input benchmark state input
+     * @param bh jmh blackhole for consuming output
+     */
+    @Benchmark
+    public void doubleFormatsScientific(final DoubleInput input, final 
Blackhole bh) {
+        runDoubleFunction(input, bh, DoubleFormats.createScientific(0, -3));
+    }
+
+    /** Benchmark testing the {@link DoubleFormats#createEngineering(int, 
int)} method.
+     * @param input benchmark state input
+     * @param bh jmh blackhole for consuming output
+     */
+    @Benchmark
+    public void doubleFormatsEngineering(final DoubleInput input, final 
Blackhole bh) {
+        runDoubleFunction(input, bh, DoubleFormats.createEngineering(0, -3));
+    }
+}
diff --git 
a/commons-geometry-examples/examples-jmh/src/main/java/org/apache/commons/geometry/examples/jmh/io/core/package-info.java
 
b/commons-geometry-examples/examples-jmh/src/main/java/org/apache/commons/geometry/examples/jmh/io/core/package-info.java
new file mode 100644
index 0000000..6f2beb2
--- /dev/null
+++ 
b/commons-geometry-examples/examples-jmh/src/main/java/org/apache/commons/geometry/examples/jmh/io/core/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Benchmarks for the components in the {@code 
org.apache.commons.geometry.io.core}
+ * package.
+ */
+
+package org.apache.commons.geometry.examples.jmh.io.core;
diff --git 
a/commons-geometry-io-core/src/main/java/org/apache/commons/geometry/io/core/utils/ParsedDouble.java
 
b/commons-geometry-io-core/src/main/java/org/apache/commons/geometry/io/core/utils/ParsedDouble.java
index c97ece0..d6d4057 100644
--- 
a/commons-geometry-io-core/src/main/java/org/apache/commons/geometry/io/core/utils/ParsedDouble.java
+++ 
b/commons-geometry-io-core/src/main/java/org/apache/commons/geometry/io/core/utils/ParsedDouble.java
@@ -39,6 +39,9 @@ final class ParsedDouble {
     /** Minus sign character. */
     private static final char MINUS_CHAR = '-';
 
+    /** Plus sign character. */
+    private static final char PLUS_CHAR = '+';
+
     /** Decimal separator character. */
     private static final char DECIMAL_SEP_CHAR = '.';
 
@@ -54,6 +57,9 @@ final class ParsedDouble {
     /** String containing the decimal digits '0' - '9' in sequence. */
     private static final String DECIMAL_DIGITS = "0123456789";
 
+    /** Initial size to use for string builder instances. */
+    private static final int INITIAL_STR_BUILDER_SIZE = 32;
+
     /** Shared instance representing the positive zero double value. */
     private static final ParsedDouble POS_ZERO = new ParsedDouble(false, 
String.valueOf(ZERO_CHAR), 0);
 
@@ -172,11 +178,9 @@ final class ParsedDouble {
         final int currentPrecision = getPrecision();
         if (currentPrecision > precision) {
             // we need to round to reduce the number of digits
-            String resultDigits = digits.substring(0, precision);
-
-            if (shouldRoundUp(precision)) {
-                resultDigits = addOne(resultDigits);
-            }
+            String resultDigits = shouldRoundUp(precision) ?
+                    addOne(digits, precision) :
+                    digits.substring(0, precision);
 
             // compute the initial result exponent
             int resultExponent = exponent + (currentPrecision - precision);
@@ -209,7 +213,7 @@ final class ParsedDouble {
     public String toPlainString(final boolean includeDecimalPlaceholder) {
         final int precision = getPrecision();
 
-        final StringBuilder sb = new StringBuilder();
+        final StringBuilder sb = new StringBuilder(INITIAL_STR_BUILDER_SIZE);
         if (negative) {
             sb.append(MINUS_CHAR);
         }
@@ -303,7 +307,7 @@ final class ParsedDouble {
     private String toScientificString(final int wholeDigits, final boolean 
includeDecimalPlaceholder) {
         final int precision = getPrecision();
 
-        final StringBuilder sb = new StringBuilder();
+        final StringBuilder sb = new StringBuilder(INITIAL_STR_BUILDER_SIZE);
         if (negative) {
             sb.append(MINUS_CHAR);
         }
@@ -367,43 +371,54 @@ final class ParsedDouble {
         }
 
         final String str = Double.toString(d);
+        final char[] strChars = str.toCharArray();
 
         // extract the different portions of the string representation
         // (since double is finite, str is guaranteed to not be empty and to 
contain a
         // single decimal point according to the Double.toString() API)
-        final boolean negative = str.charAt(0) == MINUS_CHAR;
+        final boolean negative = strChars[0] == MINUS_CHAR;
         final int digitStartIdx = negative ? 1 : 0;
 
-        final StringBuilder digitStr = new StringBuilder(str.length());
+        final char[] digitChars = new char[strChars.length];
 
         int decimalSepIdx = -1;
         int exponentIdx = -1;
+        int digitCount = 0;
+        int firstNonZeroDigitIdx = -1;
+        int lastNonZeroDigitIdx = -1;
 
-        char ch;
-        for (int i = digitStartIdx; i < str.length(); ++i) {
-            ch = str.charAt(i);
+        for (int i = digitStartIdx; i < strChars.length; ++i) {
+            final char ch = strChars[i];
 
             if (ch == DECIMAL_SEP_CHAR) {
                 decimalSepIdx = i;
             } else if (ch == EXPONENT_CHAR) {
                 exponentIdx = i;
             } else if (exponentIdx < 0) {
-                digitStr.append(ch);
+                // this is a significand digit
+                if (ch != ZERO_CHAR) {
+                    if (firstNonZeroDigitIdx < 0) {
+                        firstNonZeroDigitIdx = digitCount;
+                    }
+                    lastNonZeroDigitIdx = digitCount;
+                }
+
+                digitChars[digitCount++] = ch;
             }
         }
 
-        final int firstNonZeroIdx = findFirstNonZero(digitStr);
-        if (firstNonZeroIdx > -1) {
-            final int lastNonZeroIdx = findLastNonZero(digitStr);
-
+        if (firstNonZeroDigitIdx > -1) {
             // determine the exponent
             final int explicitExponent = exponentIdx > -1 ?
-                    Integer.parseInt(str.substring(exponentIdx + 1)) :
+                    parseExponent(str, exponentIdx + 1) :
                     0;
-            final int exponent = explicitExponent + decimalSepIdx - 
digitStartIdx - lastNonZeroIdx - 1;
+            final int exponent = explicitExponent + decimalSepIdx - 
digitStartIdx - lastNonZeroDigitIdx - 1;
 
             // get the digit string without any leading or trailing zeros
-            final String digits = digitStr.substring(firstNonZeroIdx, 
lastNonZeroIdx + 1);
+            final String digits = String.valueOf(
+                    digitChars,
+                    firstNonZeroDigitIdx,
+                    lastNonZeroDigitIdx - firstNonZeroDigitIdx + 1);
 
             return new ParsedDouble(negative, digits, exponent);
         }
@@ -414,21 +429,27 @@ final class ParsedDouble {
                 POS_ZERO;
     }
 
-    /** Return the index of the first character in the argument not equal
-     * to {@code '0'} or {@code -1} if no such character can be found.
-     * @param seq sequence to search
-     * @return the index of the first non-zero character or {@code -1} if not 
found
+    /** Parse a double exponent value from {@code seq}, starting at the {@code 
start}
+     * index and continuing through the end of the sequence.
+     * @param seq sequence to part a double exponent value from
+     * @param start start index
+     * @return parsed exponent value
      */
-    private static int findFirstNonZero(final CharSequence seq) {
-        char ch;
-        for (int i = 0; i < seq.length(); ++i) {
-            ch = seq.charAt(i);
-            if (ch != ZERO_CHAR) {
-                return i;
+    private static int parseExponent(final CharSequence seq, final int start) {
+        int exp = 0;
+        boolean neg = false;
+
+        final int len = seq.length();
+        for (int i = start; i < len; ++i) {
+            final char ch = seq.charAt(i);
+            if (ch == MINUS_CHAR) {
+                neg = !neg;
+            } else if (ch != PLUS_CHAR) {
+                exp = (exp * 10) + digitValue(ch);
             }
         }
 
-        return -1;
+        return neg ? -exp : exp;
     }
 
     /** Return the index of the last character in the argument not equal
@@ -458,44 +479,36 @@ final class ParsedDouble {
         return ch - ZERO_CHAR;
     }
 
-    /** Add one to the value of the integer represented by the given string, 
returning
-     * the result as another string. The input is assumed to contain only 
digit characters
+    /** Add one to the value of the integer represented by the substring of 
length {@code len}
+     * starting at index {@code 0}, returning the result as another string. 
The input is assumed
+     * to contain only digit characters
      * (i.e. '0' - '9'). No validation is performed.
      * @param digitStr string containing a representation of an integer
+     * @param len number of characters to use from {@code str}
      * @return string representation of the result of adding 1 to the integer 
represented
-     *      by the input
+     *      by the input substring
      */
-    private static String addOne(final String digitStr) {
-        final char[] digitChars = digitStr.toCharArray();
-        if (addOne(digitChars)) {
-            return new StringBuilder()
-                    .append(ONE_CHAR)
-                    .append(digitChars)
-                    .toString();
+    private static String addOne(final String digitStr, final int len) {
+        final char[] resultChars = new char[len + 1];
+
+        boolean carrying = true;
+        for (int i = len - 1; i >= 0; --i) {
+            final char inChar = digitStr.charAt(i);
+            final char outChar = carrying ?
+                    DECIMAL_DIGITS.charAt((digitValue(inChar) + 1) % 
DECIMAL_DIGITS.length()) :
+                    inChar;
+            resultChars[i + 1] = outChar;
+
+            if (carrying && outChar != ZERO_CHAR) {
+                carrying = false;
+            }
         }
 
-        return String.valueOf(digitChars);
-    }
-
-    /** Add one to the integer value represented by the given sequence of digit
-     * characters (i.e. '0' - '9'). The characters are modified in place. True 
is
-     * returned if the operation resulted is a carry-out of 1. False is 
returned if
-     * the result is fully contained in the passed array.
-     * @param digitChars sequence of digit characters
-     * @return true if a 1 was carried out of the operation; otherwise false
-     */
-    private static boolean addOne(final char[] digitChars) {
-        int i;
-        char c;
-        for (i = digitChars.length - 1; i >= 0; --i) {
-            c = DECIMAL_DIGITS.charAt((digitValue(digitChars[i]) + 1) % 
DECIMAL_DIGITS.length());
-            digitChars[i] = c;
-
-            if (c != ZERO_CHAR) {
-                break; // no carry over; stop
-            }
+        if (carrying) {
+            resultChars[0] = ONE_CHAR;
+            return String.valueOf(resultChars);
         }
 
-        return i < 0;
+        return String.valueOf(resultChars, 1, len);
     }
 }

Reply via email to