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 <[email protected]>
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);
}
}