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
commit 79e741d806d04a009cd48401d882cb17a5c027cd Author: Matthew Juntunen <[email protected]> AuthorDate: Mon Jul 19 22:12:39 2021 -0400 GEOMETRY-136: removing DoubleFormats utility that has been moved to commons-text --- .../jmh/io/core/DoubleFormatsPerformance.java | 183 ----- .../examples/jmh/io/core/package-info.java | 23 - .../io/core/utils/AbstractTextFormatWriter.java | 2 +- .../geometry/io/core/utils/DoubleFormats.java | 369 --------- .../geometry/io/core/utils/ParsedDouble.java | 514 ------------ .../core/utils/AbstractTextFormatWriterTest.java | 9 +- .../geometry/io/core/utils/DoubleFormatsTest.java | 894 --------------------- .../geometry/io/core/utils/ParsedDoubleTest.java | 464 ----------- .../threed/obj/ObjBoundaryWriteHandler3D.java | 3 +- .../txt/AbstractTextBoundaryWriteHandler3D.java | 3 +- .../threed/obj/ObjBoundaryWriteHandler3DTest.java | 30 +- .../io/euclidean/threed/obj/ObjWriterTest.java | 20 +- .../io/euclidean/threed/stl/TextStlWriterTest.java | 11 +- .../threed/txt/TextBoundaryWriteHandler3DTest.java | 17 +- .../threed/txt/TextFacetDefinitionWriterTest.java | 15 +- 15 files changed, 81 insertions(+), 2476 deletions(-) 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 deleted file mode 100644 index dc83014..0000000 --- a/commons-geometry-examples/examples-jmh/src/main/java/org/apache/commons/geometry/examples/jmh/io/core/DoubleFormatsPerformance.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * 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 deleted file mode 100644 index 6f2beb2..0000000 --- a/commons-geometry-examples/examples-jmh/src/main/java/org/apache/commons/geometry/examples/jmh/io/core/package-info.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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/AbstractTextFormatWriter.java b/commons-geometry-io-core/src/main/java/org/apache/commons/geometry/io/core/utils/AbstractTextFormatWriter.java index fdd94c0..f4eb918 100644 --- a/commons-geometry-io-core/src/main/java/org/apache/commons/geometry/io/core/utils/AbstractTextFormatWriter.java +++ b/commons-geometry-io-core/src/main/java/org/apache/commons/geometry/io/core/utils/AbstractTextFormatWriter.java @@ -42,7 +42,7 @@ public abstract class AbstractTextFormatWriter implements Closeable { * @param writer writer instance */ protected AbstractTextFormatWriter(final Writer writer) { - this(writer, DoubleFormats.DOUBLE_TO_STRING); + this(writer, Double::toString); } /** Construct a new instance that writes content to the given writer and uses the diff --git a/commons-geometry-io-core/src/main/java/org/apache/commons/geometry/io/core/utils/DoubleFormats.java b/commons-geometry-io-core/src/main/java/org/apache/commons/geometry/io/core/utils/DoubleFormats.java deleted file mode 100644 index 62768b8..0000000 --- a/commons-geometry-io-core/src/main/java/org/apache/commons/geometry/io/core/utils/DoubleFormats.java +++ /dev/null @@ -1,369 +0,0 @@ -/* - * 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.io.core.utils; - -import java.util.function.DoubleFunction; - -/** Class containing static utility methods and constants for formatting double values - * as strings. All instances returned by methods in this class are guaranteed to be - * thread-safe. - */ -public final class DoubleFormats { - - /** Double format function that simply calls {@link Double#toString(double)}. - */ - public static final DoubleFunction<String> DOUBLE_TO_STRING = Double::toString; - - /** Double format function that converts the argument to a float and calls - * {@link Float#toString(float)}. - */ - public static final DoubleFunction<String> FLOAT_TO_STRING = d -> Float.toString((float) d); - - /** Minimum possible decimal exponent for double values. */ - private static final int MIN_DOUBLE_EXPONENT = -325; - - /** Utility class; no instantiation. */ - private DoubleFormats() {} - - /** Return a double format function that provides similar behavior to {@link Double#toString(double)} - * but with a configurable max precision. For values with an absolute magnitude less than - * 10<sup>7</sup> and greater than or equal to 10<sup>-3</sup> (after any necessary rounding), the returned - * string is in plain, non-scientific format. All other values are in scientific format. Rounding is performed - * using {@link java.math.RoundingMode#HALF_EVEN half even} rounding. - * <table> - * <caption>Format Examples</caption> - * <tr><th>Value</th><th>(maxPrecision= 0)</th><th>(maxPrecision= 4)</th></tr> - * <tr><td>1.0E-4</td><td>1.0E-4</td><td>1.0E-4</td></tr> - * <tr><td>-0.0635</td><td>-0.0635</td><td>-0.0635</td></tr> - * <tr><td>510.751</td><td>510.751</td><td>510.8</td></tr> - * <tr><td>-123456.0</td><td>-123456.0</td><td>-123500.0</td></tr> - * <tr><td>4.20785E7</td><td>4.20785E7</td><td>4.208E7</td></tr> - * </table> - * @param maxPrecision Maximum number of significant decimal digits in strings produced by the returned formatter. - * Numbers are rounded as necessary so that the number of significant digits does not exceed this value. A - * value of {@code 0} indicates no maximum precision. - * @return double format function - * @throws IllegalArgumentException if {@code maxPrecision} is less than zero - */ - public static DoubleFunction<String> createDefault(final int maxPrecision) { - return createDefault(maxPrecision, MIN_DOUBLE_EXPONENT); - } - - /** Return a double format function that provides similar behavior to {@link Double#toString(double)} - * but with a configurable max precision and min exponent. For values with an absolute magnitude less than - * 10<sup>7</sup> and greater than or equal to 10<sup>-3</sup> (after any necessary rounding), the returned - * string is in plain, non-scientific format. All other values are in scientific format. Rounding is performed - * using {@link java.math.RoundingMode#HALF_EVEN half even} rounding. - * <table> - * <caption>Format Examples</caption> - * <tr><th>Value</th><th>(maxPrecision= 0, minExponent= -2)</th><th>(maxPrecision= 4, minExponent= -2)</th></tr> - * <tr><td>1.0E-4</td><td>0.0</td><td>0.0</td></tr> - * <tr><td>-0.0635</td><td>-0.06</td><td>-0.06</td></tr> - * <tr><td>510.751</td><td>510.75</td><td>510.8</td></tr> - * <tr><td>-123456.0</td><td>-123456.0</td><td>-123500.0</td></tr> - * <tr><td>4.20785E7</td><td>4.20785E7</td><td>4.208E7</td></tr> - * </table> - * @param maxPrecision Maximum number of significant decimal digits in strings produced by the returned formatter. - * Numbers are rounded as necessary so that the number of significant digits does not exceed this value. A - * value of {@code 0} indicates no maximum precision. - * @param minExponent Minimum decimal exponent in strings produced by the returned formatter. - * @return double format function - * @throws IllegalArgumentException if {@code maxPrecision} is less than zero - */ - public static DoubleFunction<String> createDefault(final int maxPrecision, final int minExponent) { - return new DefaultFormat(maxPrecision, minExponent); - } - - /** Return a double format function that produces strings in plain, non-scientific format. - * Rounding is performed using {@link java.math.RoundingMode#HALF_EVEN half even} rounding. - * <table> - * <caption>Format Examples</caption> - * <tr><th>Value</th><th>(maxPrecision= 0)</th><th>(maxPrecision= 4)</th></tr> - * <tr><td>1.0E-4</td><td>0.0001</td><td>0.0001</td></tr> - * <tr><td>-0.0635</td><td>-0.0635</td><td>-0.0635</td></tr> - * <tr><td>510.751</td><td>510.751</td><td>510.8</td></tr> - * <tr><td>-123456.0</td><td>-123456.0</td><td>-123500.0</td></tr> - * <tr><td>4.20785E7</td><td>42078500.0</td><td>42080000.0</td></tr> - * </table> - * @param maxPrecision Maximum number of significant decimal digits in strings produced by the returned formatter. - * Numbers are rounded as necessary so that the number of significant digits does not exceed this value. A - * value of {@code 0} indicates no maximum precision. - * @return double format function - */ - public static DoubleFunction<String> createPlain(final int maxPrecision) { - return createPlain(maxPrecision, MIN_DOUBLE_EXPONENT); - } - - /** Return a double format function that produces strings in plain, non-scientific format. - * Rounding is performed using {@link java.math.RoundingMode#HALF_EVEN half even} rounding. - * <table> - * <caption>Format Examples</caption> - * <tr><th>Value</th><th>(maxPrecision= 0, minExponent= -2)</th><th>(maxPrecision= 4, minExponent= -2)</th></tr> - * <tr><td>1.0E-4</td><td>0.0</td><td>0.0</td></tr> - * <tr><td>-0.0635</td><td>-0.06</td><td>-0.06</td></tr> - * <tr><td>510.751</td><td>510.75</td><td>510.8</td></tr> - * <tr><td>-123456.0</td><td>-123456.0</td><td>-123500.0</td></tr> - * <tr><td>4.20785E7</td><td>42078500.0</td><td>42080000.0</td></tr> - * </table> - * @param maxPrecision Maximum number of significant decimal digits in strings produced by the returned formatter. - * Numbers are rounded as necessary so that the number of significant digits does not exceed this value. A - * value of {@code 0} indicates no maximum precision. - * @param minExponent Minimum decimal exponent in strings produced by the returned formatter. - * @return double format function - */ - public static DoubleFunction<String> createPlain(final int maxPrecision, final int minExponent) { - return new PlainFormat(maxPrecision, minExponent); - } - - /** Return a double format function that produces strings in scientific format. Exponents of - * zero are not included in formatted strings. Rounding is performed using - * {@link java.math.RoundingMode#HALF_EVEN half even} rounding. - * <table> - * <caption>Format Examples</caption> - * <tr><th>Value</th><th>(maxPrecision= 0)</th><th>(maxPrecision= 4)</th></tr> - * <tr><td>1.0E-4</td><td>1.0E-4</td><td>1.0E-4</td></tr> - * <tr><td>-0.0635</td><td>-6.35E-2</td><td>-6.35E-2</td></tr> - * <tr><td>510.751</td><td>5.10751E2</td><td>5.108E2</td></tr> - * <tr><td>-123456.0</td><td>-1.23456E5</td><td>-1.235E5</td></tr> - * <tr><td>4.20785E7</td><td>4.20785E7</td><td>4.208E7</td></tr> - * </table> - * @param maxPrecision Maximum number of significant decimal digits in strings produced by the returned formatter. - * Numbers are rounded as necessary so that the number of significant digits does not exceed this value. A - * value of {@code 0} indicates no maximum precision. - * @return double format function - */ - public static DoubleFunction<String> createScientific(final int maxPrecision) { - return createScientific(maxPrecision, MIN_DOUBLE_EXPONENT); - } - - /** Return a double format function that produces strings in scientific format. Exponents of - * zero are not included in formatted strings. Rounding is performed using - * {@link java.math.RoundingMode#HALF_EVEN half even} rounding. - * <table> - * <caption>Format Examples</caption> - * <tr><th>Value</th><th>(maxPrecision= 0, minExponent= -2)</th><th>(maxPrecision= 4, minExponent= -2)</th></tr> - * <tr><td>1.0E-4</td><td>0.0</td><td>0.0</td></tr> - * <tr><td>-0.0635</td><td>-6.0E-2</td><td>-6.0E-2</td></tr> - * <tr><td>510.751</td><td>5.1075E2</td><td>5.108E2</td></tr> - * <tr><td>-123456.0</td><td>-1.23456E5</td><td>-1.235E5</td></tr> - * <tr><td>4.20785E7</td><td>4.20785E7</td><td>4.208E7</td></tr> - * </table> - * @param maxPrecision Maximum number of significant decimal digits in strings produced by the returned formatter. - * Numbers are rounded as necessary so that the number of significant digits does not exceed this value. A - * value of {@code 0} indicates no maximum precision. - * @param minExponent Minimum decimal exponent in strings produced by the returned formatter. - * @return double format function - */ - public static DoubleFunction<String> createScientific(final int maxPrecision, final int minExponent) { - return new ScientificFormat(maxPrecision, minExponent); - } - - /** Return a double format function that produces strings in - * <a href="https://en.wikipedia.org/wiki/Engineering_notation">engineering notation</a> where any exponents - * are adjusted to be multiples of 3. Exponents of zero are not included in formatted strings. Rounding is - * performed using {@link java.math.RoundingMode#HALF_EVEN half even} rounding. - * <table> - * <caption>Format Examples</caption> - * <tr><th>Value</th><th>(maxPrecision= 0)</th><th>(maxPrecision= 4)</th></tr> - * <tr><td>1.0E-4</td><td>100.0E-6</td><td>100.0E-6</td></tr> - * <tr><td>-0.0635</td><td>-63.5E-3</td><td>-63.5E-3</td></tr> - * <tr><td>510.751</td><td>510.751</td><td>510.8</td></tr> - * <tr><td>-123456.0</td><td>-123.456E3</td><td>-123.5E3</td></tr> - * <tr><td>4.20785E7</td><td>42.0785E6</td><td>42.08E6</td></tr> - * </table> - * @param maxPrecision Maximum number of significant decimal digits in strings produced by the returned formatter. - * Numbers are rounded as necessary so that the number of significant digits does not exceed this value. A - * value of {@code 0} indicates no maximum precision. - * @return double format function - */ - public static DoubleFunction<String> createEngineering(final int maxPrecision) { - return createEngineering(maxPrecision, MIN_DOUBLE_EXPONENT); - } - - /** Return a double format function that produces strings in - * <a href="https://en.wikipedia.org/wiki/Engineering_notation">engineering notation</a>, where exponents - * are adjusted to be multiples of 3. Exponents of zero are not included in formatted strings. Rounding is - * performed using {@link java.math.RoundingMode#HALF_EVEN half even} rounding. - * <table> - * <caption>Format Examples</caption> - * <tr><th>Value</th><th>(maxPrecision= 0, minExponent= -2)</th><th>(maxPrecision= 4, minExponent= -2)</th></tr> - * <tr><td>1.0E-4</td><td>0.0</td><td>0.0</td></tr> - * <tr><td>-0.0635</td><td>-60.0E-3</td><td>-60.0E-3</td></tr> - * <tr><td>510.751</td><td>510.75</td><td>510.8</td></tr> - * <tr><td>-123456.0</td><td>-123.456E3</td><td>-123.5E3</td></tr> - * <tr><td>4.20785E7</td><td>42.0785E6</td><td>42.08E6</td></tr> - * </table> - * @param maxPrecision Maximum number of significant decimal digits in strings produced by the returned formatter. - * Numbers are rounded as necessary so that the number of significant digits does not exceed this value. A - * value of {@code 0} indicates no maximum precision. - * @param minExponent Minimum decimal exponent in strings produced by the returned formatter. - * @return double format function - */ - public static DoubleFunction<String> createEngineering(final int maxPrecision, final int minExponent) { - return new EngineeringFormat(maxPrecision, minExponent); - } - - /** Base class for standard double formatting classes. - */ - private abstract static class AbstractFormat implements DoubleFunction<String> { - - /** Maximum precision to use when formatting values. */ - private final int maxPrecision; - - /** The minimum exponent to allow in the result. Value with exponents less than this are - * rounded to positive zero. - */ - private final int minExponent; - - /** Construct a new instance with the given maximum precision and minimum exponent. - * @param maxPrecision maximum number of significant decimal digits - * @param minExponent minimum decimal exponent; values less than this that do not round up - * are considered to be zero - * @throws IllegalArgumentException if {@code maxPrecision} is less than zero - */ - AbstractFormat(final int maxPrecision, final int minExponent) { - if (maxPrecision < 0) { - throw new IllegalArgumentException( - "Max precision must be greater than or equal to zero; was " + maxPrecision); - } - - this.maxPrecision = maxPrecision; - this.minExponent = minExponent; - } - - /** {@inheritDoc} */ - @Override - public String apply(final double d) { - if (Double.isFinite(d)) { - final ParsedDouble n = ParsedDouble.from(d); - - int roundExponent = Math.max(n.getExponent(), minExponent); - if (maxPrecision > 0) { - roundExponent = Math.max(n.getScientificExponent() - maxPrecision + 1, roundExponent); - } - - final ParsedDouble rounded = n.round(roundExponent); - - return formatInternal(rounded); - } - - return Double.toString(d); // NaN or infinite; use default Double toString() method - } - - /** Format the given parsed double value. - * @param val value to format - * @return formatted double value - */ - protected abstract String formatInternal(ParsedDouble val); - } - - /** Format class that produces plain decimal strings that do not use - * scientific notation. - */ - private static class PlainFormat extends AbstractFormat { - - /** Construct a new instance with the given maximum precision and minimum exponent. - * @param maxPrecision maximum number of significant decimal digits - * @param minExponent minimum decimal exponent; values less than this that do not round up - * are considered to be zero - * @throws IllegalArgumentException if {@code maxPrecision} is less than zero - */ - PlainFormat(final int maxPrecision, final int minExponent) { - super(maxPrecision, minExponent); - } - - /** {@inheritDoc} */ - @Override - protected String formatInternal(final ParsedDouble val) { - return val.toPlainString(true); - } - } - - /** Format class producing results similar to {@link Double#toString()}, with - * plain decimal notation for small numbers relatively close to zero and scientific - * notation otherwise. - */ - private static class DefaultFormat extends AbstractFormat { - - /** Decimal exponent upper bound for use of plain formatted strings. */ - private static final int UPPER_PLAIN_EXP = 7; - - /** Decimal exponent lower bound for use of plain formatted strings. */ - private static final int LOWER_PLAIN_EXP = -4; - - /** Construct a new instance with the given maximum precision and minimum exponent. - * @param maxPrecision maximum number of significant decimal digits - * @param minExponent minimum decimal exponent; values less than this that do not round up - * are considered to be zero - * @throws IllegalArgumentException if {@code maxPrecision} is less than zero - */ - DefaultFormat(final int maxPrecision, final int minExponent) { - super(maxPrecision, minExponent); - } - - /** {@inheritDoc} */ - @Override - protected String formatInternal(final ParsedDouble val) { - final int sciExp = val.getScientificExponent(); - return sciExp < UPPER_PLAIN_EXP && sciExp > LOWER_PLAIN_EXP ? - val.toPlainString(true) : - val.toScientificString(true); - } - } - - /** Format class that uses scientific notation for all values. - */ - private static class ScientificFormat extends AbstractFormat { - - /** Construct a new instance with the given maximum precision and minimum exponent. - * @param maxPrecision maximum number of significant decimal digits - * @param minExponent minimum decimal exponent; values less than this that do not round up - * are considered to be zero - * @throws IllegalArgumentException if {@code maxPrecision} is less than zero - */ - ScientificFormat(final int maxPrecision, final int minExponent) { - super(maxPrecision, minExponent); - } - - /** {@inheritDoc} */ - @Override - public String formatInternal(final ParsedDouble val) { - return val.toScientificString(true); - } - } - - /** Format class that uses engineering notation for all values. - */ - private static class EngineeringFormat extends AbstractFormat { - - /** Construct a new instance with the given maximum precision and minimum exponent. - * @param maxPrecision maximum number of significant decimal digits - * @param minExponent minimum decimal exponent; values less than this that do not round up - * are considered to be zero - * @throws IllegalArgumentException if {@code maxPrecision} is less than zero - */ - EngineeringFormat(final int maxPrecision, final int minExponent) { - super(maxPrecision, minExponent); - } - - /** {@inheritDoc} */ - @Override - public String formatInternal(final ParsedDouble val) { - return val.toEngineeringString(true); - } - } -} 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 deleted file mode 100644 index d6d4057..0000000 --- a/commons-geometry-io-core/src/main/java/org/apache/commons/geometry/io/core/utils/ParsedDouble.java +++ /dev/null @@ -1,514 +0,0 @@ -/* - * 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.io.core.utils; - -/** Simple class representing a double value parsed into separate decimal components. Each double - * is represented with - * <ul> - * <li>a boolean flag for the sign,</li> - * <li> a sequence of the digits '0' - '10' representing an unsigned integer with leading and trailing zeros - * removed, and</li> - * <li>an exponent value that when applied to the base 10 digits produces a floating point value with the - * correct magnitude.</li> - * </ul> - * <p><strong>Examples</strong></p> - * <table> - * <tr><th>Double</th><th>Negative</th><th>Digits</th><th>Exponent</th></tr> - * <tr><td>0.0</td><td>false</td><td>"0"</td><td>0</td></tr> - * <tr><td>1.2</td><td>false</td><td>"12"</td><td>-1</td></tr> - * <tr><td>-0.00971</td><td>true</td><td>"971"</td><td>-5</td></tr> - * <tr><td>56300</td><td>true</td><td>"563"</td><td>2</td></tr> - * </table> - */ -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 = '.'; - - /** Exponent character. */ - private static final char EXPONENT_CHAR = 'E'; - - /** Zero digit character. */ - private static final char ZERO_CHAR = '0'; - - /** One digit character. */ - private static final char ONE_CHAR = '1'; - - /** 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); - - /** Shared instance representing the negative zero double value. */ - private static final ParsedDouble NEG_ZERO = new ParsedDouble(true, String.valueOf(ZERO_CHAR), 0); - - /** True if the value is negative. */ - private final boolean negative; - - /** String containing the significant base-10 digits for the value. */ - private final String digits; - - /** Exponent for the value. */ - private final int exponent; - - /** Construct a new instance from its parts. - * @param negative true if the value is negative - * @param digits string containing significant digits - * @param exponent exponent value - */ - ParsedDouble(final boolean negative, final String digits, final int exponent) { - this.negative = negative; - this.digits = digits; - this.exponent = exponent; - } - - /** Return true if the value is negative. - * @return true if the value is negative - */ - public boolean isNegative() { - return negative; - } - - /** Get a string containing the significant digits of the value. If the value is - * {@code 0}, then the returned string is {@code "0"}. Otherwise, the string contains - * one or more characters with the first and last characters not equal to {@code '0'}. - * @return string containing the significant digits of the value - */ - public String getDigits() { - return digits; - } - - /** Get the exponent value. This exponent produces a floating point value with the - * correct magnitude when applied to the unsigned integer represented by the {@link #getDigits() digit} - * string. - * @return exponent value - */ - public int getExponent() { - return exponent; - } - - /** Return true if the value is equal to zero. The sign field is ignored, - * meaning that this method will return true for both {@code +0} and {@code -0}. - * @return true if the value is equal to zero - */ - public boolean isZero() { - return getPrecision() == 1 && digits.charAt(0) == ZERO_CHAR; - } - - /** Return the precision of this instance, meaning the number of significant decimal - * digits in the representation. - * @return the precision of this instance - */ - public int getPrecision() { - return digits.length(); - } - - /** Get the exponent that would be used when representing this number in scientific - * notation (i.e., with a single non-zero digit in front of the decimal point. - * @return the exponent that would be used when representing this number in scientific - * notation - */ - public int getScientificExponent() { - return getPrecision() + exponent - 1; - } - - /** Round the instance to the given decimal exponent position using - * {@link java.math.RoundingMode#HALF_EVEN half-even rounding}. For example, a value of {@code -2} - * will round the instance to the digit at the position 10<sup>-2</sup> (i.e. to the closest multiple of 0.01). - * A new instance is returned if the rounding operation results in a new value. - * @param roundExponent exponent defining the decimal place to round to - * @return result of the rounding operation - */ - public ParsedDouble round(final int roundExponent) { - if (roundExponent > exponent) { - final int precision = getPrecision(); - final int max = precision + exponent; - - if (roundExponent < max) { - return maxPrecision(max - roundExponent); - } else if (roundExponent == max && shouldRoundUp(0)) { - return new ParsedDouble(negative, "1", roundExponent); - } - - return POS_ZERO; - } - - return this; - } - - /** Return the value as close as possible to this instance with <em>at most</em> the given number - * of significant digits (i.e. precision). If this instance already has a precision less than or equal - * to the argument, it is returned directly. If the given precision requires a reduction in the number - * of digits, then the value is rounded using {@link java.math.RoundingMode#HALF_EVEN half-even rounding} - * and a new instance is returned with the rounded value. - * @param precision maximum number of significant digits to include - * @return the instance as close as possible to this value with at most the given number of - * significant digits - * @throws IllegalArgumentException if {@code precision} is less than 1 - */ - public ParsedDouble maxPrecision(final int precision) { - if (precision < 1) { - throw new IllegalArgumentException("Precision must be greater than zero; was " + precision); - } - - final int currentPrecision = getPrecision(); - if (currentPrecision > precision) { - // we need to round to reduce the number of digits - String resultDigits = shouldRoundUp(precision) ? - addOne(digits, precision) : - digits.substring(0, precision); - - // compute the initial result exponent - int resultExponent = exponent + (currentPrecision - precision); - - // remove zeros from the end of the integer if present, adjusting the - // exponent as needed - final int lastNonZeroIdx = findLastNonZero(resultDigits); - if (lastNonZeroIdx < resultDigits.length() - 1) { - resultExponent += resultDigits.length() - 1 - lastNonZeroIdx; - resultDigits = resultDigits.substring(0, lastNonZeroIdx + 1); - } - - return new ParsedDouble(negative, resultDigits, resultExponent); - } - - return this; // no change needed - } - - /** Return a string representation of the value with no exponent field. Ex: - * <pre> - * 10 = "10.0" - * 1e-6 = "0.000001" - * 1e11 = "100000000000.0" - * </pre> - * @param includeDecimalPlaceholder if true, then the returned string will contain - * the decimal placeholder ".0" when no fractional value is present, similar - * to {@link Double#toString(double)} - * @return a string representation of the value with no exponent field - */ - public String toPlainString(final boolean includeDecimalPlaceholder) { - final int precision = getPrecision(); - - final StringBuilder sb = new StringBuilder(INITIAL_STR_BUILDER_SIZE); - if (negative) { - sb.append(MINUS_CHAR); - } - - if (exponent < 0) { - final int diff = precision + exponent; - - // add whole digits, using a beginning placeholder zero if - // needed - int i; - for (i = 0; i < diff; ++i) { - sb.append(digits.charAt(i)); - } - if (i == 0) { - sb.append(ZERO_CHAR); - } - - // decimal separator - sb.append(DECIMAL_SEP_CHAR); - - // add placeholder fraction zeros if needed - for (int j = 0; j > diff; --j) { - sb.append(ZERO_CHAR); - } - - // fraction digits - sb.append(digits, i, precision); - } else { - sb.append(digits); - - for (int i = 0; i < exponent; ++i) { - sb.append(ZERO_CHAR); - } - - if (includeDecimalPlaceholder) { - sb.append(DECIMAL_SEP_CHAR) - .append(ZERO_CHAR); - } - } - - return sb.toString(); - } - - /** Return a string representation of the value in scientific notation. If the exponent field - * is equal to zero, it is not included in the result. Ex: - * <pre> - * 0 = "0.0" - * 10 = "1.0E1" - * 1e-6 = "1.0E-6" - * 1e11 = "1.0E11" - * </pre> - * @param includeDecimalPlaceholder if true, then the returned string will contain - * the decimal placeholder ".0" when no fractional value is present, similar - * to {@link Double#toString(double)} - * @return a string representation of the value in scientific notation - */ - public String toScientificString(final boolean includeDecimalPlaceholder) { - return toScientificString(1, includeDecimalPlaceholder); - } - - /** Return a string representation of the value in engineering notation. This is similar - * to {@link #toScientificString(boolean) scientific notation} but with the exponent forced - * to be a multiple of 3, allowing easier alignment with SI prefixes. If the exponent field - * is equal to zero, it is not included in the result. - * <pre> - * 0 = "0.0" - * 10 = "10.0" - * 1e-6 = "1.0E-6" - * 1e11 = "100.0E9" - * </pre> - * @param includeDecimalPlaceholder if true, then the returned string will contain - * the decimal placeholder ".0" when no fractional value is present, similar - * to {@link Double#toString(double)} - * @return a string representation of the value in engineering notation - */ - public String toEngineeringString(final boolean includeDecimalPlaceholder) { - final int wholeDigits = 1 + Math.floorMod(getPrecision() + exponent - 1, 3); - return toScientificString(wholeDigits, includeDecimalPlaceholder); - } - - /** Return a string representation of the value in scientific notation using the - * given number of whole digits. If the exponent field of the result is zero, it - * is not included in the returned string. - * @param wholeDigits number of whole digits to use in the output - * @param includeDecimalPlaceholder if true, then the returned string will contain - * the decimal placeholder ".0" when no fractional value is present, similar - * to {@link Double#toString(double)} - * @return a string representation of the value in scientific notation using the - * given number of whole digits - */ - private String toScientificString(final int wholeDigits, final boolean includeDecimalPlaceholder) { - final int precision = getPrecision(); - - final StringBuilder sb = new StringBuilder(INITIAL_STR_BUILDER_SIZE); - if (negative) { - sb.append(MINUS_CHAR); - } - - if (precision <= wholeDigits) { - // not enough digits to meet the requested number of whole digits; - // we'll need to pad with zeros - sb.append(digits); - - for (int i = precision; i < wholeDigits; ++i) { - sb.append(ZERO_CHAR); - } - - if (includeDecimalPlaceholder) { - sb.append(DECIMAL_SEP_CHAR) - .append(ZERO_CHAR); - } - } else { - // we'll need a fractional portion - sb.append(digits, 0, wholeDigits) - .append(DECIMAL_SEP_CHAR) - .append(digits, wholeDigits, precision); - } - - // add the exponent but only if non-zero - final int resultExponent = exponent + precision - wholeDigits; - if (resultExponent != 0) { - sb.append(EXPONENT_CHAR) - .append(resultExponent); - } - - return sb.toString(); - } - - /** Return true if a rounding operation at the given index should round up. - * @param idx index of the digit to round; must be a valid index into {@code digits} - * @return true if a rounding operation at the given index should round up - */ - private boolean shouldRoundUp(final int idx) { - // Round up in the following cases: - // 1. The digit at the index is greater than 5. - // 2. The digit at the index is 5 and there are additional (non-zero) - // digits after it. - // 3. The digit is 5, there are no additional digits afterward, - // and the digit before it is odd (half-even rounding). - final int precision = getPrecision(); - final int roundValue = digitValue(digits.charAt(idx)); - - return roundValue > 5 || (roundValue == 5 && - (idx < precision - 1 || (idx > 0 && digitValue(digits.charAt(idx - 1)) % 2 != 0))); - } - - /** Construct a new instance from the given double value. - * @param d double value - * @return a new instance containing the parsed components of the given double value - * @throws IllegalArgumentException if {@code d} is {@code NaN} or infinite - */ - public static ParsedDouble from(final double d) { - if (!Double.isFinite(d)) { - throw new IllegalArgumentException("Double is not finite"); - } - - 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 = strChars[0] == MINUS_CHAR; - final int digitStartIdx = negative ? 1 : 0; - - final char[] digitChars = new char[strChars.length]; - - int decimalSepIdx = -1; - int exponentIdx = -1; - int digitCount = 0; - int firstNonZeroDigitIdx = -1; - int lastNonZeroDigitIdx = -1; - - 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) { - // this is a significand digit - if (ch != ZERO_CHAR) { - if (firstNonZeroDigitIdx < 0) { - firstNonZeroDigitIdx = digitCount; - } - lastNonZeroDigitIdx = digitCount; - } - - digitChars[digitCount++] = ch; - } - } - - if (firstNonZeroDigitIdx > -1) { - // determine the exponent - final int explicitExponent = exponentIdx > -1 ? - parseExponent(str, exponentIdx + 1) : - 0; - final int exponent = explicitExponent + decimalSepIdx - digitStartIdx - lastNonZeroDigitIdx - 1; - - // get the digit string without any leading or trailing zeros - final String digits = String.valueOf( - digitChars, - firstNonZeroDigitIdx, - lastNonZeroDigitIdx - firstNonZeroDigitIdx + 1); - - return new ParsedDouble(negative, digits, exponent); - } - - // no non-zero digits, so value is zero - return negative ? - NEG_ZERO : - POS_ZERO; - } - - /** 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 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 neg ? -exp : exp; - } - - /** Return the index of the last 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 last non-zero character or {@code -1} if not found - */ - private static int findLastNonZero(final CharSequence seq) { - int i; - char ch; - for (i = seq.length() - 1; i >= 0; --i) { - ch = seq.charAt(i); - if (ch != ZERO_CHAR) { - break; - } - } - - return i; - } - - /** Get the numeric value of the given digit character. No validation of the - * character type is performed. - * @param ch digit character - * @return numeric value of the digit character, ex: '1' = 1 - */ - private static int digitValue(final char ch) { - return ch - ZERO_CHAR; - } - - /** 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 substring - */ - 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; - } - } - - if (carrying) { - resultChars[0] = ONE_CHAR; - return String.valueOf(resultChars); - } - - return String.valueOf(resultChars, 1, len); - } -} diff --git a/commons-geometry-io-core/src/test/java/org/apache/commons/geometry/io/core/utils/AbstractTextFormatWriterTest.java b/commons-geometry-io-core/src/test/java/org/apache/commons/geometry/io/core/utils/AbstractTextFormatWriterTest.java index 316804a..5f9516f 100644 --- a/commons-geometry-io-core/src/test/java/org/apache/commons/geometry/io/core/utils/AbstractTextFormatWriterTest.java +++ b/commons-geometry-io-core/src/test/java/org/apache/commons/geometry/io/core/utils/AbstractTextFormatWriterTest.java @@ -19,6 +19,9 @@ package org.apache.commons.geometry.io.core.utils; import java.io.IOException; import java.io.StringWriter; import java.io.Writer; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.util.Locale; import java.util.function.DoubleFunction; import org.apache.commons.geometry.io.core.test.CloseCountWriter; @@ -35,7 +38,7 @@ class AbstractTextFormatWriterTest { try (TestWriter writer = new TestWriter(out)) { // assert Assertions.assertEquals("\n", writer.getLineSeparator()); - Assertions.assertSame(DoubleFormats.DOUBLE_TO_STRING, writer.getDoubleFormat()); + Assertions.assertNotNull(writer.getDoubleFormat()); Assertions.assertSame(out, writer.getWriter()); } } @@ -71,7 +74,9 @@ class AbstractTextFormatWriterTest { writer.setLineSeparator("\r\n"); - final DoubleFunction<String> df = DoubleFormats.createPlain(0, -2); + final DecimalFormat fmt = new DecimalFormat("0.00", DecimalFormatSymbols.getInstance(Locale.ENGLISH)); + + final DoubleFunction<String> df = fmt::format; writer.setDoubleFormat(df); // act diff --git a/commons-geometry-io-core/src/test/java/org/apache/commons/geometry/io/core/utils/DoubleFormatsTest.java b/commons-geometry-io-core/src/test/java/org/apache/commons/geometry/io/core/utils/DoubleFormatsTest.java deleted file mode 100644 index ce1ad2d..0000000 --- a/commons-geometry-io-core/src/test/java/org/apache/commons/geometry/io/core/utils/DoubleFormatsTest.java +++ /dev/null @@ -1,894 +0,0 @@ -/* - * 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.io.core.utils; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.function.BiFunction; -import java.util.function.DoubleFunction; -import java.util.function.IntFunction; - -import org.apache.commons.geometry.core.GeometryTestUtils; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -class DoubleFormatsTest { - - private static final double[] EXAMPLE_VALUES = { - 0.0001, -0.0635, 510.751, -123456.0, 42078500.0 - }; - - @Test - void testDoubleToString() { - // arrange - final DoubleFunction<String> fmt = DoubleFormats.DOUBLE_TO_STRING; - - // act/assert - checkFormatSpecial(fmt); - - checkFormat(fmt, 0.00001, "1.0E-5"); - checkFormat(fmt, -0.0001, "-1.0E-4"); - checkFormat(fmt, 0.001, "0.001"); - checkFormat(fmt, -0.01, "-0.01"); - checkFormat(fmt, 0.1, "0.1"); - checkFormat(fmt, -0.0, "-0.0"); - checkFormat(fmt, 0.0, "0.0"); - checkFormat(fmt, -1.0, "-1.0"); - checkFormat(fmt, 10.0, "10.0"); - checkFormat(fmt, -100.0, "-100.0"); - checkFormat(fmt, 1000.0, "1000.0"); - checkFormat(fmt, -10000.0, "-10000.0"); - checkFormat(fmt, 100000.0, "100000.0"); - checkFormat(fmt, -1000000.0, "-1000000.0"); - checkFormat(fmt, 10000000.0, "1.0E7"); - checkFormat(fmt, -100000000.0, "-1.0E8"); - - checkFormat(fmt, 0.5 * Float.MAX_VALUE, "1.7014117331926443E38"); - checkFormat(fmt, -1.0 / 1.9175e20, "-5.2151238591916555E-21"); - - checkFormat(fmt, Double.MAX_VALUE, "1.7976931348623157E308"); - checkFormat(fmt, Double.MIN_VALUE, "4.9E-324"); - checkFormat(fmt, Double.MIN_NORMAL, "2.2250738585072014E-308"); - checkFormat(fmt, Math.PI, "3.141592653589793"); - checkFormat(fmt, Math.E, "2.718281828459045"); - } - - @Test - void testFloatToString() { - // arrange - final DoubleFunction<String> fmt = DoubleFormats.FLOAT_TO_STRING; - - // act/assert - checkFormatSpecial(fmt); - - checkFormat(fmt, 0.00001, "1.0E-5"); - checkFormat(fmt, -0.0001, "-1.0E-4"); - checkFormat(fmt, 0.001, "0.001"); - checkFormat(fmt, -0.01, "-0.01"); - checkFormat(fmt, 0.1, "0.1"); - checkFormat(fmt, -0.0, "-0.0"); - checkFormat(fmt, 0.0, "0.0"); - checkFormat(fmt, -1.0, "-1.0"); - checkFormat(fmt, 10.0, "10.0"); - checkFormat(fmt, -100.0, "-100.0"); - checkFormat(fmt, 1000.0, "1000.0"); - checkFormat(fmt, -10000.0, "-10000.0"); - checkFormat(fmt, 100000.0, "100000.0"); - checkFormat(fmt, -1000000.0, "-1000000.0"); - checkFormat(fmt, 10000000.0, "1.0E7"); - checkFormat(fmt, -100000000.0, "-1.0E8"); - - checkFormat(fmt, 0.5 * Float.MAX_VALUE, "1.7014117E38"); - checkFormat(fmt, -1.0 / 1.9175e20, "-5.2151238E-21"); - - checkFormat(fmt, Double.MAX_VALUE, "Infinity"); - checkFormat(fmt, Double.MIN_VALUE, "0.0"); - checkFormat(fmt, Double.MIN_NORMAL, "0.0"); - checkFormat(fmt, Math.PI, "3.1415927"); - checkFormat(fmt, Math.E, "2.7182817"); - } - - @Test - void testDefault_noPrecisionLimit_noMinExponent() { - // arrange - final int maxPrecision = 0; - - // act - final DoubleFunction<String> fmt = DoubleFormats.createDefault(maxPrecision); - - // act/assert - checkFormatSpecial(fmt); - - checkFormat(fmt, 0.00001, "1.0E-5"); - checkFormat(fmt, -0.0001, "-1.0E-4"); - checkFormat(fmt, 0.001, "0.001"); - checkFormat(fmt, -0.01, "-0.01"); - checkFormat(fmt, 0.1, "0.1"); - checkFormat(fmt, -0.0, "-0.0"); - checkFormat(fmt, 0.0, "0.0"); - checkFormat(fmt, -1.0, "-1.0"); - checkFormat(fmt, 10.0, "10.0"); - checkFormat(fmt, -100.0, "-100.0"); - checkFormat(fmt, 1000.0, "1000.0"); - checkFormat(fmt, -10000.0, "-10000.0"); - checkFormat(fmt, 100000.0, "100000.0"); - checkFormat(fmt, -1000000.0, "-1000000.0"); - checkFormat(fmt, 10000000.0, "1.0E7"); - checkFormat(fmt, -100000000.0, "-1.0E8"); - - checkFormat(fmt, 1.25e-3, "0.00125"); - checkFormat(fmt, -9.975e-4, "-9.975E-4"); - checkFormat(fmt, -9_999_999, "-9999999.0"); - checkFormat(fmt, 1.00001e7, "1.00001E7"); - - checkFormat(fmt, Double.MAX_VALUE, "1.7976931348623157E308"); - checkFormat(fmt, Double.MIN_VALUE, "4.9E-324"); - checkFormat(fmt, Double.MIN_NORMAL, "2.2250738585072014E-308"); - checkFormat(fmt, Math.PI, "3.141592653589793"); - checkFormat(fmt, Math.E, "2.718281828459045"); - } - - @Test - void testDefault_noPrecisionLimit_withMinExponent() { - // arrange - final int maxPrecision = 0; - final int minExponent = -3; - - final DoubleFunction<String> fmt = DoubleFormats.createDefault(maxPrecision, minExponent); - - // act/assert - checkFormatSpecial(fmt); - - checkFormat(fmt, 0.00001, "0.0"); - checkFormat(fmt, -0.0001, "0.0"); - checkFormat(fmt, 0.001, "0.001"); - checkFormat(fmt, -0.01, "-0.01"); - checkFormat(fmt, 0.1, "0.1"); - checkFormat(fmt, -0.0, "-0.0"); - checkFormat(fmt, 0.0, "0.0"); - checkFormat(fmt, -1.0, "-1.0"); - checkFormat(fmt, 10.0, "10.0"); - checkFormat(fmt, -100.0, "-100.0"); - checkFormat(fmt, 1000.0, "1000.0"); - checkFormat(fmt, -10000.0, "-10000.0"); - checkFormat(fmt, 100000.0, "100000.0"); - checkFormat(fmt, -1000000.0, "-1000000.0"); - checkFormat(fmt, 10000000.0, "1.0E7"); - checkFormat(fmt, -100000000.0, "-1.0E8"); - - checkFormat(fmt, 1.25e-3, "0.001"); - checkFormat(fmt, -9.975e-4, "-0.001"); - checkFormat(fmt, -9_999_999, "-9999999.0"); - checkFormat(fmt, 1.00001e7, "1.00001E7"); - - checkFormat(fmt, Double.MAX_VALUE, "1.7976931348623157E308"); - checkFormat(fmt, Double.MIN_VALUE, "0.0"); - checkFormat(fmt, Double.MIN_NORMAL, "0.0"); - checkFormat(fmt, Math.PI, "3.142"); - checkFormat(fmt, Math.E, "2.718"); - } - - @Test - void testDefault_withPrecisionLimit_noMinExponent() { - // arrange - final int maxPrecision = 4; - final int minExponent = Integer.MIN_VALUE; - - final DoubleFunction<String> fmt = DoubleFormats.createDefault(maxPrecision, minExponent); - - // act/assert - checkFormatSpecial(fmt); - - checkFormat(fmt, 0.00001, "1.0E-5"); - checkFormat(fmt, -0.0001, "-1.0E-4"); - checkFormat(fmt, 0.001, "0.001"); - checkFormat(fmt, -0.01, "-0.01"); - checkFormat(fmt, 0.1, "0.1"); - checkFormat(fmt, -0.0, "-0.0"); - checkFormat(fmt, 0.0, "0.0"); - checkFormat(fmt, -1.0, "-1.0"); - checkFormat(fmt, 10.0, "10.0"); - checkFormat(fmt, -100.0, "-100.0"); - checkFormat(fmt, 1000.0, "1000.0"); - checkFormat(fmt, -10000.0, "-10000.0"); - checkFormat(fmt, 100000.0, "100000.0"); - checkFormat(fmt, -1000000.0, "-1000000.0"); - checkFormat(fmt, 10000000.0, "1.0E7"); - checkFormat(fmt, -100000000.0, "-1.0E8"); - - checkFormat(fmt, 12345.01, "12350.0"); - checkFormat(fmt, 1.2345, "1.234"); - - checkFormat(fmt, 1.25e-3, "0.00125"); - checkFormat(fmt, -9.975e-4, "-9.975E-4"); - checkFormat(fmt, -9_999_999, "-1.0E7"); - checkFormat(fmt, 1.00001e7, "1.0E7"); - - checkFormat(fmt, Double.MAX_VALUE, "1.798E308"); - checkFormat(fmt, Double.MIN_VALUE, "4.9E-324"); - checkFormat(fmt, Double.MIN_NORMAL, "2.225E-308"); - checkFormat(fmt, Math.PI, "3.142"); - checkFormat(fmt, Math.E, "2.718"); - } - - @Test - void testDefault_withPrecisionLimit_withMinExponent() { - // arrange - final int maxPrecision = 3; - final int minExponent = -3; - - final DoubleFunction<String> fmt = DoubleFormats.createDefault(maxPrecision, minExponent); - - // act/assert - checkFormatSpecial(fmt); - - checkFormat(fmt, 0.00001, "0.0"); - checkFormat(fmt, -0.0001, "0.0"); - checkFormat(fmt, 0.001, "0.001"); - checkFormat(fmt, -0.01, "-0.01"); - checkFormat(fmt, 0.1, "0.1"); - checkFormat(fmt, -0.0, "-0.0"); - checkFormat(fmt, 0.0, "0.0"); - checkFormat(fmt, -1.0, "-1.0"); - checkFormat(fmt, 10.0, "10.0"); - checkFormat(fmt, -100.0, "-100.0"); - checkFormat(fmt, 1000.0, "1000.0"); - checkFormat(fmt, -10000.0, "-10000.0"); - checkFormat(fmt, 100000.0, "100000.0"); - checkFormat(fmt, -1000000.0, "-1000000.0"); - checkFormat(fmt, 10000000.0, "1.0E7"); - checkFormat(fmt, -100000000.0, "-1.0E8"); - - checkFormat(fmt, 1.25e-3, "0.001"); - checkFormat(fmt, -9.975e-4, "-0.001"); - checkFormat(fmt, -9_999_999, "-1.0E7"); - checkFormat(fmt, 1.00001e7, "1.0E7"); - - checkFormat(fmt, Double.MAX_VALUE, "1.8E308"); - checkFormat(fmt, Double.MIN_VALUE, "0.0"); - checkFormat(fmt, Double.MIN_NORMAL, "0.0"); - checkFormat(fmt, Math.PI, "3.14"); - checkFormat(fmt, Math.E, "2.72"); - } - - @Test - void testPlain_noPrecisionLimit_noMinExponent() { - // arrange - final int maxPrecision = 0; - - final DoubleFunction<String> fmt = DoubleFormats.createPlain(maxPrecision); - - // act/assert - checkFormatSpecial(fmt); - - checkFormat(fmt, 0.00001, "0.00001"); - checkFormat(fmt, -0.0001, "-0.0001"); - checkFormat(fmt, 0.001, "0.001"); - checkFormat(fmt, -0.01, "-0.01"); - checkFormat(fmt, 0.1, "0.1"); - checkFormat(fmt, -0.0, "-0.0"); - checkFormat(fmt, 0.0, "0.0"); - checkFormat(fmt, -1.0, "-1.0"); - checkFormat(fmt, 10.0, "10.0"); - checkFormat(fmt, -100.0, "-100.0"); - checkFormat(fmt, 1000.0, "1000.0"); - checkFormat(fmt, -10000.0, "-10000.0"); - checkFormat(fmt, 100000.0, "100000.0"); - checkFormat(fmt, -1000000.0, "-1000000.0"); - checkFormat(fmt, 10000000.0, "10000000.0"); - checkFormat(fmt, -100000000.0, "-100000000.0"); - - checkFormat(fmt, 1.25e-3, "0.00125"); - checkFormat(fmt, -9.975e-4, "-0.0009975"); - checkFormat(fmt, -9_999_999, "-9999999.0"); - checkFormat(fmt, 1.00001e7, "10000100.0"); - - checkFormat(fmt, Float.MAX_VALUE, "340282346638528860000000000000000000000.0"); - checkFormat(fmt, Float.MIN_VALUE, "0.000000000000000000000000000000000000000000001401298464324817"); - checkFormat(fmt, Float.MIN_NORMAL, "0.000000000000000000000000000000000000011754943508222875"); - checkFormat(fmt, Math.PI, "3.141592653589793"); - checkFormat(fmt, Math.E, "2.718281828459045"); - } - - @Test - void testPlain_noPrecisionLimit_withMinExponent() { - // arrange - final int maxPrecision = 0; - final int minExponent = -2; - - final DoubleFunction<String> fmt = DoubleFormats.createPlain(maxPrecision, minExponent); - - // act/assert - checkFormatSpecial(fmt); - - checkFormat(fmt, 0.00001, "0.0"); - checkFormat(fmt, -0.0001, "0.0"); - checkFormat(fmt, 0.001, "0.0"); - checkFormat(fmt, -0.01, "-0.01"); - checkFormat(fmt, 0.1, "0.1"); - checkFormat(fmt, -0.0, "-0.0"); - checkFormat(fmt, 0.0, "0.0"); - checkFormat(fmt, -1.0, "-1.0"); - checkFormat(fmt, 10.0, "10.0"); - checkFormat(fmt, -100.0, "-100.0"); - checkFormat(fmt, 1000.0, "1000.0"); - checkFormat(fmt, -10000.0, "-10000.0"); - checkFormat(fmt, 100000.0, "100000.0"); - checkFormat(fmt, -1000000.0, "-1000000.0"); - checkFormat(fmt, 10000000.0, "10000000.0"); - checkFormat(fmt, -100000000.0, "-100000000.0"); - - checkFormat(fmt, 1.25e-3, "0.0"); - checkFormat(fmt, -9.975e-4, "0.0"); - checkFormat(fmt, -9_999_999, "-9999999.0"); - checkFormat(fmt, 1.00001e7, "10000100.0"); - - checkFormat(fmt, Float.MAX_VALUE, "340282346638528860000000000000000000000.0"); - checkFormat(fmt, Float.MIN_VALUE, "0.0"); - checkFormat(fmt, Float.MIN_NORMAL, "0.0"); - checkFormat(fmt, Math.PI, "3.14"); - checkFormat(fmt, Math.E, "2.72"); - } - - @Test - void testPlain_withPrecisionLimit_noMinExponent() { - // arrange - final int maxPrecision = 3; - final int minExponent = Integer.MIN_VALUE; - - final DoubleFunction<String> fmt = DoubleFormats.createPlain(maxPrecision, minExponent); - - // act/assert - checkFormatSpecial(fmt); - - checkFormat(fmt, 0.00001, "0.00001"); - checkFormat(fmt, -0.0001, "-0.0001"); - checkFormat(fmt, 0.001, "0.001"); - checkFormat(fmt, -0.01, "-0.01"); - checkFormat(fmt, 0.1, "0.1"); - checkFormat(fmt, -0.0, "-0.0"); - checkFormat(fmt, 0.0, "0.0"); - checkFormat(fmt, -1.0, "-1.0"); - checkFormat(fmt, 10.0, "10.0"); - checkFormat(fmt, -100.0, "-100.0"); - checkFormat(fmt, 1000.0, "1000.0"); - checkFormat(fmt, -10000.0, "-10000.0"); - checkFormat(fmt, 100000.0, "100000.0"); - checkFormat(fmt, -1000000.0, "-1000000.0"); - checkFormat(fmt, 10000000.0, "10000000.0"); - checkFormat(fmt, -100000000.0, "-100000000.0"); - - checkFormat(fmt, 1.25e-3, "0.00125"); - checkFormat(fmt, -9.975e-4, "-0.000998"); - checkFormat(fmt, -9_999_999, "-10000000.0"); - checkFormat(fmt, 1.00001e7, "10000000.0"); - - checkFormat(fmt, Float.MAX_VALUE, "340000000000000000000000000000000000000.0"); - checkFormat(fmt, Float.MIN_VALUE, "0.0000000000000000000000000000000000000000000014"); - checkFormat(fmt, Float.MIN_NORMAL, "0.0000000000000000000000000000000000000118"); - checkFormat(fmt, Math.PI, "3.14"); - checkFormat(fmt, Math.E, "2.72"); - } - - @Test - void testPlain_withPrecisionLimit_withMinExponent() { - // arrange - final int maxPrecision = 4; - final int minExponent = -2; - - final DoubleFunction<String> fmt = DoubleFormats.createPlain(maxPrecision, minExponent); - - // act/assert - checkFormatSpecial(fmt); - - checkFormat(fmt, 0.00001, "0.0"); - checkFormat(fmt, -0.0001, "0.0"); - checkFormat(fmt, 0.001, "0.0"); - checkFormat(fmt, -0.01, "-0.01"); - checkFormat(fmt, 0.1, "0.1"); - checkFormat(fmt, -0.0, "-0.0"); - checkFormat(fmt, 0.0, "0.0"); - checkFormat(fmt, -1.0, "-1.0"); - checkFormat(fmt, 10.0, "10.0"); - checkFormat(fmt, -100.0, "-100.0"); - checkFormat(fmt, 1000.0, "1000.0"); - checkFormat(fmt, -10000.0, "-10000.0"); - checkFormat(fmt, 100000.0, "100000.0"); - checkFormat(fmt, -1000000.0, "-1000000.0"); - checkFormat(fmt, 10000000.0, "10000000.0"); - checkFormat(fmt, -100000000.0, "-100000000.0"); - - checkFormat(fmt, 1.25e-3, "0.0"); - checkFormat(fmt, -9.975e-4, "0.0"); - checkFormat(fmt, -9_999_999, "-10000000.0"); - checkFormat(fmt, 1.00001e7, "10000000.0"); - - checkFormat(fmt, Float.MAX_VALUE, "340300000000000000000000000000000000000.0"); - checkFormat(fmt, Float.MIN_VALUE, "0.0"); - checkFormat(fmt, Float.MIN_NORMAL, "0.0"); - checkFormat(fmt, Math.PI, "3.14"); - checkFormat(fmt, Math.E, "2.72"); - } - - @Test - void testScientific_noPrecisionLimit_noMinExponent() { - // arrange - final int maxPrecision = 0; - - // act - final DoubleFunction<String> fmt = DoubleFormats.createScientific(maxPrecision); - - // act/assert - checkFormatSpecial(fmt); - - checkFormat(fmt, 0.00001, "1.0E-5"); - checkFormat(fmt, -0.0001, "-1.0E-4"); - checkFormat(fmt, 0.001, "1.0E-3"); - checkFormat(fmt, -0.01, "-1.0E-2"); - checkFormat(fmt, 0.1, "1.0E-1"); - checkFormat(fmt, -0.0, "-0.0"); - checkFormat(fmt, 0.0, "0.0"); - checkFormat(fmt, -1.0, "-1.0"); - checkFormat(fmt, 10.0, "1.0E1"); - checkFormat(fmt, -100.0, "-1.0E2"); - checkFormat(fmt, 1000.0, "1.0E3"); - checkFormat(fmt, -10000.0, "-1.0E4"); - checkFormat(fmt, 100000.0, "1.0E5"); - checkFormat(fmt, -1000000.0, "-1.0E6"); - checkFormat(fmt, 10000000.0, "1.0E7"); - checkFormat(fmt, -100000000.0, "-1.0E8"); - - checkFormat(fmt, 1.25e-3, "1.25E-3"); - checkFormat(fmt, -9.975e-4, "-9.975E-4"); - checkFormat(fmt, -9_999_999, "-9.999999E6"); - checkFormat(fmt, 1.00001e7, "1.00001E7"); - - checkFormat(fmt, Double.MAX_VALUE, "1.7976931348623157E308"); - checkFormat(fmt, Double.MIN_VALUE, "4.9E-324"); - checkFormat(fmt, Double.MIN_NORMAL, "2.2250738585072014E-308"); - checkFormat(fmt, Math.PI, "3.141592653589793"); - checkFormat(fmt, Math.E, "2.718281828459045"); - } - - @Test - void testScientific_noPrecisionLimit_withMinExponent() { - // arrange - final int maxPrecision = 0; - final int minExponent = -3; - - final DoubleFunction<String> fmt = DoubleFormats.createScientific(maxPrecision, minExponent); - - // act/assert - checkFormatSpecial(fmt); - - checkFormat(fmt, 0.00001, "0.0"); - checkFormat(fmt, -0.0001, "0.0"); - checkFormat(fmt, 0.001, "1.0E-3"); - checkFormat(fmt, -0.01, "-1.0E-2"); - checkFormat(fmt, 0.1, "1.0E-1"); - checkFormat(fmt, -0.0, "-0.0"); - checkFormat(fmt, 0.0, "0.0"); - checkFormat(fmt, -1.0, "-1.0"); - checkFormat(fmt, 10.0, "1.0E1"); - checkFormat(fmt, -100.0, "-1.0E2"); - checkFormat(fmt, 1000.0, "1.0E3"); - checkFormat(fmt, -10000.0, "-1.0E4"); - checkFormat(fmt, 100000.0, "1.0E5"); - checkFormat(fmt, -1000000.0, "-1.0E6"); - checkFormat(fmt, 10000000.0, "1.0E7"); - checkFormat(fmt, -100000000.0, "-1.0E8"); - - checkFormat(fmt, 1.25e-3, "1.0E-3"); - checkFormat(fmt, -9.975e-4, "-1.0E-3"); - checkFormat(fmt, -9_999_999, "-9.999999E6"); - checkFormat(fmt, 1.00001e7, "1.00001E7"); - - checkFormat(fmt, Double.MAX_VALUE, "1.7976931348623157E308"); - checkFormat(fmt, Double.MIN_VALUE, "0.0"); - checkFormat(fmt, Double.MIN_NORMAL, "0.0"); - checkFormat(fmt, Math.PI, "3.142"); - checkFormat(fmt, Math.E, "2.718"); - } - - @Test - void testScientific_withPrecisionLimit_noMinExponent() { - // arrange - final int maxPrecision = 3; - final int minExponent = Integer.MIN_VALUE; - - final DoubleFunction<String> fmt = DoubleFormats.createScientific(maxPrecision, minExponent); - - // act/assert - checkFormatSpecial(fmt); - - checkFormat(fmt, 0.00001, "1.0E-5"); - checkFormat(fmt, -0.0001, "-1.0E-4"); - checkFormat(fmt, 0.001, "1.0E-3"); - checkFormat(fmt, -0.01, "-1.0E-2"); - checkFormat(fmt, 0.1, "1.0E-1"); - checkFormat(fmt, -0.0, "-0.0"); - checkFormat(fmt, 0.0, "0.0"); - checkFormat(fmt, -1.0, "-1.0"); - checkFormat(fmt, 10.0, "1.0E1"); - checkFormat(fmt, -100.0, "-1.0E2"); - checkFormat(fmt, 1000.0, "1.0E3"); - checkFormat(fmt, -10000.0, "-1.0E4"); - checkFormat(fmt, 100000.0, "1.0E5"); - checkFormat(fmt, -1000000.0, "-1.0E6"); - checkFormat(fmt, 10000000.0, "1.0E7"); - checkFormat(fmt, -100000000.0, "-1.0E8"); - - checkFormat(fmt, 12345.01, "1.23E4"); - checkFormat(fmt, 1.2345, "1.23"); - - checkFormat(fmt, 1.25e-3, "1.25E-3"); - checkFormat(fmt, -9.975e-4, "-9.98E-4"); - checkFormat(fmt, -9_999_999, "-1.0E7"); - checkFormat(fmt, 1.00001e7, "1.0E7"); - - checkFormat(fmt, Double.MAX_VALUE, "1.8E308"); - checkFormat(fmt, Double.MIN_VALUE, "4.9E-324"); - checkFormat(fmt, Double.MIN_NORMAL, "2.23E-308"); - checkFormat(fmt, Math.PI, "3.14"); - checkFormat(fmt, Math.E, "2.72"); - } - - @Test - void testScientific_withPrecisionLimit_withMinExponent() { - // arrange - final int maxPrecision = 3; - final int minExponent = -3; - - final DoubleFunction<String> fmt = DoubleFormats.createScientific(maxPrecision, minExponent); - - // act/assert - checkFormatSpecial(fmt); - - checkFormat(fmt, 0.00001, "0.0"); - checkFormat(fmt, -0.0001, "0.0"); - checkFormat(fmt, 0.001, "1.0E-3"); - checkFormat(fmt, -0.01, "-1.0E-2"); - checkFormat(fmt, 0.1, "1.0E-1"); - checkFormat(fmt, -0.0, "-0.0"); - checkFormat(fmt, 0.0, "0.0"); - checkFormat(fmt, -1.0, "-1.0"); - checkFormat(fmt, 10.0, "1.0E1"); - checkFormat(fmt, -100.0, "-1.0E2"); - checkFormat(fmt, 1000.0, "1.0E3"); - checkFormat(fmt, -10000.0, "-1.0E4"); - checkFormat(fmt, 100000.0, "1.0E5"); - checkFormat(fmt, -1000000.0, "-1.0E6"); - checkFormat(fmt, 10000000.0, "1.0E7"); - checkFormat(fmt, -100000000.0, "-1.0E8"); - - checkFormat(fmt, 1.25e-3, "1.0E-3"); - checkFormat(fmt, -9.975e-4, "-1.0E-3"); - checkFormat(fmt, -9_999_999, "-1.0E7"); - checkFormat(fmt, 1.00001e7, "1.0E7"); - - checkFormat(fmt, Double.MAX_VALUE, "1.8E308"); - checkFormat(fmt, Double.MIN_VALUE, "0.0"); - checkFormat(fmt, Double.MIN_NORMAL, "0.0"); - checkFormat(fmt, Math.PI, "3.14"); - checkFormat(fmt, Math.E, "2.72"); - } - - @Test - void testEngineering_noPrecisionLimit_noMinExponent() { - // arrange - final int maxPrecision = 0; - - // act - final DoubleFunction<String> fmt = DoubleFormats.createEngineering(maxPrecision); - - // act/assert - checkFormatSpecial(fmt); - - checkFormat(fmt, 0.00001, "10.0E-6"); - checkFormat(fmt, -0.0001, "-100.0E-6"); - checkFormat(fmt, 0.001, "1.0E-3"); - checkFormat(fmt, -0.01, "-10.0E-3"); - checkFormat(fmt, 0.1, "100.0E-3"); - checkFormat(fmt, -0.0, "-0.0"); - checkFormat(fmt, 0.0, "0.0"); - checkFormat(fmt, -1.0, "-1.0"); - checkFormat(fmt, 10.0, "10.0"); - checkFormat(fmt, -100.0, "-100.0"); - checkFormat(fmt, 1000.0, "1.0E3"); - checkFormat(fmt, -10000.0, "-10.0E3"); - checkFormat(fmt, 100000.0, "100.0E3"); - checkFormat(fmt, -1000000.0, "-1.0E6"); - checkFormat(fmt, 10000000.0, "10.0E6"); - checkFormat(fmt, -100000000.0, "-100.0E6"); - - checkFormat(fmt, 1.25e-3, "1.25E-3"); - checkFormat(fmt, -9.975e-4, "-997.5E-6"); - checkFormat(fmt, -9_999_999, "-9.999999E6"); - checkFormat(fmt, 1.00001e7, "10.0001E6"); - - checkFormat(fmt, Double.MAX_VALUE, "179.76931348623157E306"); - checkFormat(fmt, Double.MIN_VALUE, "4.9E-324"); - checkFormat(fmt, Double.MIN_NORMAL, "22.250738585072014E-309"); - checkFormat(fmt, Math.PI, "3.141592653589793"); - checkFormat(fmt, Math.E, "2.718281828459045"); - } - - @Test - void testEngineering_noPrecisionLimit_withMinExponent() { - // arrange - final int maxPrecision = 0; - final int minExponent = -3; - - final DoubleFunction<String> fmt = DoubleFormats.createEngineering(maxPrecision, minExponent); - - // act/assert - checkFormatSpecial(fmt); - - checkFormat(fmt, 0.00001, "0.0"); - checkFormat(fmt, -0.0001, "0.0"); - checkFormat(fmt, 0.001, "1.0E-3"); - checkFormat(fmt, -0.01, "-10.0E-3"); - checkFormat(fmt, 0.1, "100.0E-3"); - checkFormat(fmt, -0.0, "-0.0"); - checkFormat(fmt, 0.0, "0.0"); - checkFormat(fmt, -1.0, "-1.0"); - checkFormat(fmt, 10.0, "10.0"); - checkFormat(fmt, -100.0, "-100.0"); - checkFormat(fmt, 1000.0, "1.0E3"); - checkFormat(fmt, -10000.0, "-10.0E3"); - checkFormat(fmt, 100000.0, "100.0E3"); - checkFormat(fmt, -1000000.0, "-1.0E6"); - checkFormat(fmt, 10000000.0, "10.0E6"); - checkFormat(fmt, -100000000.0, "-100.0E6"); - - checkFormat(fmt, 1.25e-3, "1.0E-3"); - checkFormat(fmt, -9.975e-4, "-1.0E-3"); - checkFormat(fmt, -9_999_999, "-9.999999E6"); - checkFormat(fmt, 1.00001e7, "10.0001E6"); - - checkFormat(fmt, Double.MAX_VALUE, "179.76931348623157E306"); - checkFormat(fmt, Double.MIN_VALUE, "0.0"); - checkFormat(fmt, Double.MIN_NORMAL, "0.0"); - checkFormat(fmt, Math.PI, "3.142"); - checkFormat(fmt, Math.E, "2.718"); - } - - @Test - void testEngineering_withPrecisionLimit_noMinExponent() { - // arrange - final int maxPrecision = 3; - final int minExponent = Integer.MIN_VALUE; - - final DoubleFunction<String> fmt = DoubleFormats.createEngineering(maxPrecision, minExponent); - - // act/assert - checkFormatSpecial(fmt); - - checkFormat(fmt, 0.00001, "10.0E-6"); - checkFormat(fmt, -0.0001, "-100.0E-6"); - checkFormat(fmt, 0.001, "1.0E-3"); - checkFormat(fmt, -0.01, "-10.0E-3"); - checkFormat(fmt, 0.1, "100.0E-3"); - checkFormat(fmt, -0.0, "-0.0"); - checkFormat(fmt, 0.0, "0.0"); - checkFormat(fmt, -1.0, "-1.0"); - checkFormat(fmt, 10.0, "10.0"); - checkFormat(fmt, -100.0, "-100.0"); - checkFormat(fmt, 1000.0, "1.0E3"); - checkFormat(fmt, -10000.0, "-10.0E3"); - checkFormat(fmt, 100000.0, "100.0E3"); - checkFormat(fmt, -1000000.0, "-1.0E6"); - checkFormat(fmt, 10000000.0, "10.0E6"); - checkFormat(fmt, -100000000.0, "-100.0E6"); - - checkFormat(fmt, 1.25e-3, "1.25E-3"); - checkFormat(fmt, -9.975e-4, "-998.0E-6"); - checkFormat(fmt, -9_999_999, "-10.0E6"); - checkFormat(fmt, 1.00001e7, "10.0E6"); - - checkFormat(fmt, Double.MAX_VALUE, "180.0E306"); - checkFormat(fmt, Double.MIN_VALUE, "4.9E-324"); - checkFormat(fmt, Double.MIN_NORMAL, "22.3E-309"); - checkFormat(fmt, Math.PI, "3.14"); - checkFormat(fmt, Math.E, "2.72"); - } - - @Test - void testEngineering_withPrecisionLimit_withMinExponent() { - // arrange - final int maxPrecision = 3; - final int minExponent = -3; - - final DoubleFunction<String> fmt = DoubleFormats.createEngineering(maxPrecision, minExponent); - - // act/assert - checkFormatSpecial(fmt); - - checkFormat(fmt, 0.00001, "0.0"); - checkFormat(fmt, -0.0001, "0.0"); - checkFormat(fmt, 0.001, "1.0E-3"); - checkFormat(fmt, -0.01, "-10.0E-3"); - checkFormat(fmt, 0.1, "100.0E-3"); - checkFormat(fmt, -0.0, "-0.0"); - checkFormat(fmt, 0.0, "0.0"); - checkFormat(fmt, -1.0, "-1.0"); - checkFormat(fmt, 10.0, "10.0"); - checkFormat(fmt, -100.0, "-100.0"); - checkFormat(fmt, 1000.0, "1.0E3"); - checkFormat(fmt, -10000.0, "-10.0E3"); - checkFormat(fmt, 100000.0, "100.0E3"); - checkFormat(fmt, -1000000.0, "-1.0E6"); - checkFormat(fmt, 10000000.0, "10.0E6"); - checkFormat(fmt, -100000000.0, "-100.0E6"); - - checkFormat(fmt, 1.25e-3, "1.0E-3"); - checkFormat(fmt, -9.975e-4, "-1.0E-3"); - checkFormat(fmt, -9_999_999, "-10.0E6"); - checkFormat(fmt, 1.00001e7, "10.0E6"); - - checkFormat(fmt, Double.MAX_VALUE, "180.0E306"); - checkFormat(fmt, Double.MIN_VALUE, "0.0"); - checkFormat(fmt, Double.MIN_NORMAL, "0.0"); - checkFormat(fmt, Math.PI, "3.14"); - checkFormat(fmt, Math.E, "2.72"); - } - - @Test - void testPrecisionValidation() { - // arrange - final List<IntFunction<DoubleFunction<String>>> fns = Arrays.asList( - DoubleFormats::createDefault, - p -> DoubleFormats.createDefault(p, Integer.MIN_VALUE), - DoubleFormats::createPlain, - p -> DoubleFormats.createPlain(p, Integer.MIN_VALUE), - DoubleFormats::createScientific, - p -> DoubleFormats.createScientific(p, Integer.MIN_VALUE), - DoubleFormats::createEngineering, - p -> DoubleFormats.createEngineering(p, Integer.MIN_VALUE) - ); - - final String msg = "Max precision must be greater than or equal to zero; was -1"; - - // act/assert - for (final IntFunction<DoubleFunction<String>> fn : fns) { - GeometryTestUtils.assertThrowsWithMessage( - () -> fn.apply(-1), - IllegalArgumentException.class, msg); - } - } - - /** Utility method used to generate the tables of format examples in the Javadocs. - * This helps to ensure accuracy and consistency in the documentation. The HTML tables - * are printed to a file and can then be copied into the correct locations in the source. - * @throws IOException - */ - // @Test - public void generateExampleTables() throws IOException { - final Path output = Paths.get("target/format-examples.txt"); - - final List<String> lines = new ArrayList<>(); - - lines.add("Default - one arg"); - lines.add(generateOneArgExamplesTable(DoubleFormats::createDefault)); - - lines.add("Default - two arg"); - lines.add(generateTwoArgExamplesTable(DoubleFormats::createDefault)); - - lines.add("Plain - one arg"); - lines.add(generateOneArgExamplesTable(DoubleFormats::createPlain)); - - lines.add("Plain - two arg"); - lines.add(generateTwoArgExamplesTable(DoubleFormats::createPlain)); - - lines.add("Scientific - one arg"); - lines.add(generateOneArgExamplesTable(DoubleFormats::createScientific)); - - lines.add("Scientific - two arg"); - lines.add(generateTwoArgExamplesTable(DoubleFormats::createScientific)); - - lines.add("Engineering - one arg"); - lines.add(generateOneArgExamplesTable(DoubleFormats::createEngineering)); - - lines.add("Engineering - two arg"); - lines.add(generateTwoArgExamplesTable(DoubleFormats::createEngineering)); - - Files.write(output, lines); - } - - private static String generateOneArgExamplesTable(final IntFunction<DoubleFunction<String>> fn) { - final int aMaxPrecision = 0; - final int bMaxPrecision = 4; - - final DoubleFunction<String> aFmt = fn.apply(aMaxPrecision); - final DoubleFunction<String> bFmt = fn.apply(bMaxPrecision); - - final String descTemplate = "(maxPrecision= %d)"; - - return generateExamplesTable( - Arrays.asList(aFmt, bFmt), - Arrays.asList(String.format(descTemplate, aMaxPrecision), String.format(descTemplate, bMaxPrecision)) - ); - } - - private static String generateTwoArgExamplesTable(final BiFunction<Integer, Integer, DoubleFunction<String>> fn) { - final int aMaxPrecision = 0; - final int aMinExponent = -2; - - final int bMaxPrecision = 4; - final int bMinExponent = -2; - - final DoubleFunction<String> aFmt = fn.apply(aMaxPrecision, aMinExponent); - final DoubleFunction<String> bFmt = fn.apply(bMaxPrecision, bMinExponent); - - final String descTemplate = "(maxPrecision= %d, minExponent= %d)"; - - return generateExamplesTable( - Arrays.asList(aFmt, bFmt), - Arrays.asList( - String.format(descTemplate, aMaxPrecision, aMinExponent), - String.format(descTemplate, bMaxPrecision, bMinExponent)) - ); - } - - private static String generateExamplesTable(final List<DoubleFunction<String>> fmts, - final List<String> fmtDescriptions) { - final StringBuilder sb = new StringBuilder(); - - sb.append("<table>\n") - .append(" <tr><th>Value</th>"); - - for (String desc : fmtDescriptions) { - sb.append("<th>") - .append(desc) - .append("</th>"); - } - sb.append("</tr>\n"); - - for (double value : EXAMPLE_VALUES) { - sb.append(" <tr><td>") - .append(value) - .append("</td>"); - - for (DoubleFunction<String> fmt : fmts) { - sb.append("<td>") - .append(fmt.apply(value)) - .append("</td>"); - } - - sb.append("</tr>\n"); - } - - sb.append("</table>"); - - return sb.toString(); - } - - private static void checkFormat(final DoubleFunction<String> fmt, final double d, final String str) { - Assertions.assertEquals(str, fmt.apply(d)); - } - - private static void checkFormatSpecial(final DoubleFunction<String> fmt) { - checkFormat(fmt, Double.NaN, "NaN"); - checkFormat(fmt, Double.POSITIVE_INFINITY, "Infinity"); - checkFormat(fmt, Double.NEGATIVE_INFINITY, "-Infinity"); - } -} diff --git a/commons-geometry-io-core/src/test/java/org/apache/commons/geometry/io/core/utils/ParsedDoubleTest.java b/commons-geometry-io-core/src/test/java/org/apache/commons/geometry/io/core/utils/ParsedDoubleTest.java deleted file mode 100644 index 975c09f..0000000 --- a/commons-geometry-io-core/src/test/java/org/apache/commons/geometry/io/core/utils/ParsedDoubleTest.java +++ /dev/null @@ -1,464 +0,0 @@ -/* - * 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.io.core.utils; - -import java.math.BigDecimal; -import java.math.MathContext; -import java.math.RoundingMode; -import java.util.function.BiFunction; - -import org.apache.commons.geometry.core.GeometryTestUtils; -import org.apache.commons.rng.UniformRandomProvider; -import org.apache.commons.rng.simple.RandomSource; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -class ParsedDoubleTest { - - @Test - void testFrom() { - // act/assert - checkFrom(0.0, "0", 0); - - checkFrom(1.0, "1", 0); - checkFrom(10.0, "1", 1); - checkFrom(100.0, "1", 2); - checkFrom(1000.0, "1", 3); - checkFrom(10000.0, "1", 4); - - checkFrom(0.1, "1", -1); - checkFrom(0.01, "1", -2); - checkFrom(0.001, "1", -3); - checkFrom(0.0001, "1", -4); - checkFrom(0.00001, "1", -5); - - checkFrom(1.2, "12", -1); - checkFrom(0.00971, "971", -5); - checkFrom(56300, "563", 2); - - checkFrom(123.0, "123", 0); - checkFrom(1230.0, "123", 1); - checkFrom(12300.0, "123", 2); - checkFrom(123000.0, "123", 3); - - checkFrom(12.3, "123", -1); - checkFrom(1.23, "123", -2); - checkFrom(0.123, "123", -3); - checkFrom(0.0123, "123", -4); - - checkFrom(1.987654321e270, "1987654321", 261); - checkFrom(1.987654321e-270, "1987654321", -279); - - checkFrom(Math.PI, "3141592653589793", -15); - checkFrom(Math.E, "2718281828459045", -15); - - checkFrom(Double.MAX_VALUE, "17976931348623157", 292); - checkFrom(Double.MIN_VALUE, "49", -325); - checkFrom(Double.MIN_NORMAL, "22250738585072014", -324); - } - - @Test - void testFrom_notFinite() { - // arrange - final String msg = "Double is not finite"; - - // act/assert - GeometryTestUtils.assertThrowsWithMessage(() -> ParsedDouble.from(Double.NaN), - IllegalArgumentException.class, msg); - GeometryTestUtils.assertThrowsWithMessage(() -> ParsedDouble.from(Double.NEGATIVE_INFINITY), - IllegalArgumentException.class, msg); - GeometryTestUtils.assertThrowsWithMessage(() -> ParsedDouble.from(Double.POSITIVE_INFINITY), - IllegalArgumentException.class, msg); - } - - @Test - void testIsZero() { - // act/assert - Assertions.assertTrue(ParsedDouble.from(0.0).isZero()); - Assertions.assertTrue(ParsedDouble.from(-0.0).isZero()); - - Assertions.assertFalse(ParsedDouble.from(1.0).isZero()); - Assertions.assertFalse(ParsedDouble.from(-1.0).isZero()); - - Assertions.assertFalse(ParsedDouble.from(Double.MIN_NORMAL).isZero()); - Assertions.assertFalse(ParsedDouble.from(-Double.MIN_NORMAL).isZero()); - - Assertions.assertFalse(ParsedDouble.from(Double.MAX_VALUE).isZero()); - Assertions.assertFalse(ParsedDouble.from(-Double.MIN_VALUE).isZero()); - } - - @Test - void testRound_one() { - // arrange - final ParsedDouble a = ParsedDouble.from(1e-10); - final ParsedDouble b = ParsedDouble.from(-1); - final ParsedDouble c = ParsedDouble.from(1e10); - - // act/assert - assertParsedDouble(a.round(-11), false, "1", -10); - assertParsedDouble(a.round(-10), false, "1", -10); - assertParsedDouble(a.round(-9), false, "0", 0); - - assertParsedDouble(b.round(-1), true, "1", 0); - assertParsedDouble(b.round(0), true, "1", 0); - assertParsedDouble(b.round(1), false, "0", 0); - - assertParsedDouble(c.round(9), false, "1", 10); - assertParsedDouble(c.round(10), false, "1", 10); - assertParsedDouble(c.round(11), false, "0", 0); - } - - @Test - void testRound_nine() { - // arrange - final ParsedDouble a = ParsedDouble.from(9e-10); - final ParsedDouble b = ParsedDouble.from(-9); - final ParsedDouble c = ParsedDouble.from(9e10); - - // act/assert - assertParsedDouble(a.round(-11), false, "9", -10); - assertParsedDouble(a.round(-10), false, "9", -10); - assertParsedDouble(a.round(-9), false, "1", -9); - - assertParsedDouble(b.round(-1), true, "9", 0); - assertParsedDouble(b.round(0), true, "9", 0); - assertParsedDouble(b.round(1), true, "1", 1); - - assertParsedDouble(c.round(9), false, "9", 10); - assertParsedDouble(c.round(10), false, "9", 10); - assertParsedDouble(c.round(11), false, "1", 11); - } - - @Test - void testRound_mixed() { - // arrange - final ParsedDouble a = ParsedDouble.from(9.94e-10); - final ParsedDouble b = ParsedDouble.from(-3.1415); - final ParsedDouble c = ParsedDouble.from(5.55e10); - - // act/assert - assertParsedDouble(a.round(-13), false, "994", -12); - assertParsedDouble(a.round(-12), false, "994", -12); - assertParsedDouble(a.round(-11), false, "99", -11); - assertParsedDouble(a.round(-10), false, "1", -9); - assertParsedDouble(a.round(-9), false, "1", -9); - assertParsedDouble(a.round(-8), false, "0", 0); - - assertParsedDouble(b.round(-5), true, "31415", -4); - assertParsedDouble(b.round(-4), true, "31415", -4); - assertParsedDouble(b.round(-3), true, "3142", -3); - assertParsedDouble(b.round(-2), true, "314", -2); - assertParsedDouble(b.round(-1), true, "31", -1); - assertParsedDouble(b.round(0), true, "3", 0); - assertParsedDouble(b.round(1), false, "0", 0); - assertParsedDouble(b.round(2), false, "0", 0); - - assertParsedDouble(c.round(7), false, "555", 8); - assertParsedDouble(c.round(8), false, "555", 8); - assertParsedDouble(c.round(9), false, "56", 9); - assertParsedDouble(c.round(10), false, "6", 10); - assertParsedDouble(c.round(11), false, "1", 11); - assertParsedDouble(c.round(12), false, "0", 0); - } - - @Test - void testMaxPrecision() { - // arrange - final ParsedDouble d = ParsedDouble.from(1.02576552); - - // act - assertParsedDouble(d.maxPrecision(10), false, "102576552", -8); - assertParsedDouble(d.maxPrecision(9), false, "102576552", -8); - assertParsedDouble(d.maxPrecision(8), false, "10257655", -7); - assertParsedDouble(d.maxPrecision(7), false, "1025766", -6); - assertParsedDouble(d.maxPrecision(6), false, "102577", -5); - assertParsedDouble(d.maxPrecision(5), false, "10258", -4); - assertParsedDouble(d.maxPrecision(4), false, "1026", -3); - assertParsedDouble(d.maxPrecision(3), false, "103", -2); - assertParsedDouble(d.maxPrecision(2), false, "1", 0); - assertParsedDouble(d.maxPrecision(1), false, "1", 0); - } - - @Test - void testMaxPrecision_carry() { - // arrange - final ParsedDouble d = ParsedDouble.from(-999.0999e50); - - // act - assertParsedDouble(d.maxPrecision(8), true, "9990999", 46); - assertParsedDouble(d.maxPrecision(7), true, "9990999", 46); - assertParsedDouble(d.maxPrecision(6), true, "9991", 49); - assertParsedDouble(d.maxPrecision(5), true, "9991", 49); - assertParsedDouble(d.maxPrecision(4), true, "9991", 49); - assertParsedDouble(d.maxPrecision(3), true, "999", 50); - assertParsedDouble(d.maxPrecision(2), true, "1", 53); - assertParsedDouble(d.maxPrecision(1), true, "1", 53); - } - - @Test - void testMaxPrecision_halfEvenRounding() { - // act/assert - // Test values taken from RoundingMode.HALF_EVEN javadocs - assertParsedDouble(ParsedDouble.from(5.5).maxPrecision(1), false, "6", 0); - assertParsedDouble(ParsedDouble.from(2.5).maxPrecision(1), false, "2", 0); - assertParsedDouble(ParsedDouble.from(1.6).maxPrecision(1), false, "2", 0); - assertParsedDouble(ParsedDouble.from(1.1).maxPrecision(1), false, "1", 0); - assertParsedDouble(ParsedDouble.from(1.0).maxPrecision(1), false, "1", 0); - - assertParsedDouble(ParsedDouble.from(-1.0).maxPrecision(1), true, "1", 0); - assertParsedDouble(ParsedDouble.from(-1.1).maxPrecision(1), true, "1", 0); - assertParsedDouble(ParsedDouble.from(-1.6).maxPrecision(1), true, "2", 0); - assertParsedDouble(ParsedDouble.from(-2.5).maxPrecision(1), true, "2", 0); - assertParsedDouble(ParsedDouble.from(-5.5).maxPrecision(1), true, "6", 0); - } - - @Test - void testMaxPrecision_singleDigits() { - // act - assertParsedDouble(ParsedDouble.from(9.0).maxPrecision(1), false, "9", 0); - assertParsedDouble(ParsedDouble.from(1.0).maxPrecision(1), false, "1", 0); - assertParsedDouble(ParsedDouble.from(0.0).maxPrecision(1), false, "0", 0); - assertParsedDouble(ParsedDouble.from(-0.0).maxPrecision(1), true, "0", 0); - assertParsedDouble(ParsedDouble.from(-1.0).maxPrecision(1), true, "1", 0); - assertParsedDouble(ParsedDouble.from(-9.0).maxPrecision(1), true, "9", 0); - } - - @Test - void testMaxPrecision_random() { - // arrange - final UniformRandomProvider rand = RandomSource.create(RandomSource.XO_RO_SHI_RO_128_PP, 0L); - - double d; - int precision; - ParsedDouble result; - MathContext ctx; - for (int i = 0; i < 10_000; ++i) { - d = createRandomDouble(rand); - precision = rand.nextInt(20) + 1; - ctx = new MathContext(precision, RoundingMode.HALF_EVEN); - - // act - result = ParsedDouble.from(d).maxPrecision(precision); - - // assert - Assertions.assertEquals(new BigDecimal(Double.toString(d), ctx).doubleValue(), - Double.parseDouble(result.toScientificString(true))); - } - } - - @Test - void testMaxPrecision_invalidArg() { - // arrange - final ParsedDouble d = ParsedDouble.from(10); - final String baseMsg = "Precision must be greater than zero; was "; - - // act/assert - GeometryTestUtils.assertThrowsWithMessage( - () -> d.maxPrecision(0), IllegalArgumentException.class, baseMsg + "0"); - GeometryTestUtils.assertThrowsWithMessage( - () -> d.maxPrecision(-1), IllegalArgumentException.class, baseMsg + "-1"); - } - - @Test - void testToPlainString() { - // act/assert - checkToPlainString(0.0, "0.0", "0"); - checkToPlainString(1.0, "1.0", "1"); - checkToPlainString(1.5, "1.5"); - - checkToPlainString(0.000123, "0.000123"); - checkToPlainString(12300, "12300.0", "12300"); - - checkToPlainString(Math.PI, "3.141592653589793"); - checkToPlainString(Math.E, "2.718281828459045"); - - checkToPlainString(12345.6789, "12345.6789"); - checkToPlainString(1.23e12, "1230000000000.0", "1230000000000"); - checkToPlainString(1.23e-12, "0.00000000000123"); - } - - @Test - void testToScientificString() { - // act/assert - checkToScientificString(0.0, "0.0", "0"); - checkToScientificString(1.0, "1.0", "1"); - checkToScientificString(1.5, "1.5"); - - checkToScientificString(0.000123, "1.23E-4"); - checkToScientificString(12300, "1.23E4"); - - checkToScientificString(Math.PI, "3.141592653589793"); - checkToScientificString(Math.E, "2.718281828459045"); - - checkToScientificString(Double.MAX_VALUE, "1.7976931348623157E308"); - checkToScientificString(Double.MIN_VALUE, "4.9E-324"); - checkToScientificString(Double.MIN_NORMAL, "2.2250738585072014E-308"); - } - - @Test - void testToEngineeringString() { - // act/assert - checkToEngineeringString(0.0, "0.0", "0"); - checkToEngineeringString(1.0, "1.0", "1"); - checkToEngineeringString(1.5, "1.5"); - - checkToEngineeringString(10, "10.0", "10"); - - checkToEngineeringString(0.000000123, "123.0E-9", "123E-9"); - checkToEngineeringString(12300000, "12.3E6"); - - checkToEngineeringString(Math.PI, "3.141592653589793"); - checkToEngineeringString(Math.E, "2.718281828459045"); - - checkToEngineeringString(Double.MAX_VALUE, "179.76931348623157E306"); - checkToEngineeringString(Double.MIN_VALUE, "4.9E-324"); - checkToEngineeringString(Double.MIN_NORMAL, "22.250738585072014E-309"); - } - - @Test - void testStringMethodAccuracy_sequence() { - // arrange - final double min = -1000; - final double max = 1000; - final double delta = 0.1; - - Assertions.assertEquals(10.0, Double.parseDouble(ParsedDouble.from(10.0).toEngineeringString(true))); - - for (double d = min; d <= max; d += delta) { - // act/assert - Assertions.assertEquals(d, Double.parseDouble(ParsedDouble.from(d).toScientificString(true))); - Assertions.assertEquals(d, Double.parseDouble(ParsedDouble.from(d).toScientificString(false))); - - Assertions.assertEquals(d, Double.parseDouble(ParsedDouble.from(d).toEngineeringString(true))); - Assertions.assertEquals(d, Double.parseDouble(ParsedDouble.from(d).toEngineeringString(false))); - - Assertions.assertEquals(d, Double.parseDouble(ParsedDouble.from(d).toPlainString(true))); - Assertions.assertEquals(d, Double.parseDouble(ParsedDouble.from(d).toPlainString(false))); - } - } - - @Test - void testStringMethodAccuracy_random() { - // arrange - final UniformRandomProvider rand = RandomSource.create(RandomSource.XO_RO_SHI_RO_128_PP, 0L); - - double d; - for (int i = 0; i < 10_000; ++i) { - d = createRandomDouble(rand); - - // act/assert - Assertions.assertEquals(d, Double.parseDouble(ParsedDouble.from(d).toScientificString(true))); - Assertions.assertEquals(d, Double.parseDouble(ParsedDouble.from(d).toScientificString(false))); - - Assertions.assertEquals(d, Double.parseDouble(ParsedDouble.from(d).toEngineeringString(true))); - Assertions.assertEquals(d, Double.parseDouble(ParsedDouble.from(d).toEngineeringString(false))); - - Assertions.assertEquals(d, Double.parseDouble(ParsedDouble.from(d).toPlainString(true))); - Assertions.assertEquals(d, Double.parseDouble(ParsedDouble.from(d).toPlainString(false))); - } - } - - private static void checkFrom(final double d, final String digits, final int exponent) { - final boolean negative = Math.signum(d) < 0; - - assertParsedDouble(ParsedDouble.from(d), negative, digits, exponent); - assertParsedDouble(ParsedDouble.from(-d), !negative, digits, exponent); - } - - private static void checkToPlainString(final double d, final String expected) { - checkToPlainString(d, expected, expected); - } - - private static void checkToPlainString(final double d, final String withPlaceholder, - final String withoutPlaceholder) { - checkToStringMethod(d, withPlaceholder, withoutPlaceholder, ParsedDouble::toPlainString); - } - - private static void checkToScientificString(final double d, final String expected) { - checkToScientificString(d, expected, expected); - } - - private static void checkToScientificString(final double d, final String withPlaceholder, - final String withoutPlaceholder) { - checkToStringMethod(d, withPlaceholder, withoutPlaceholder, ParsedDouble::toScientificString); - } - - private static void checkToEngineeringString(final double d, final String expected) { - checkToEngineeringString(d, expected, expected); - } - - private static void checkToEngineeringString(final double d, final String withPlaceholder, - final String withoutPlaceholder) { - checkToStringMethod(d, withPlaceholder, withoutPlaceholder, ParsedDouble::toEngineeringString); - - // check the exponent value to make sure it is a multiple of 3 - final String pos = ParsedDouble.from(d).toEngineeringString(true); - final int posEIdx = pos.indexOf('E'); - if (posEIdx > -1) { - Assertions.assertEquals(0, Integer.parseInt(pos.substring(posEIdx + 1)) % 3); - } - - final String neg = ParsedDouble.from(-d).toEngineeringString(true); - final int negEIdx = neg.indexOf('E'); - if (negEIdx > -1) { - Assertions.assertEquals(0, Integer.parseInt(neg.substring(negEIdx + 1)) % 3); - } - } - - private static void checkToStringMethod(final double d, final String withPlaceholder, - final String withoutPlaceholder, final BiFunction<ParsedDouble, Boolean, String> fn) { - final ParsedDouble pos = ParsedDouble.from(d); - - final String posWith = fn.apply(pos, true); - Assertions.assertEquals(withPlaceholder, posWith); - assertDoubleString(d, posWith); - - final String posWithout = fn.apply(pos, false); - Assertions.assertEquals(withoutPlaceholder, posWithout); - assertDoubleString(d, posWithout); - - final ParsedDouble neg = ParsedDouble.from(-d); - - final String negWith = fn.apply(neg, true); - Assertions.assertEquals("-" + withPlaceholder, negWith); - assertDoubleString(-d, negWith); - - final String negWithout = fn.apply(neg, false); - Assertions.assertEquals("-" + withoutPlaceholder, negWithout); - assertDoubleString(-d, negWithout); - } - - private static void assertDoubleString(final double d, final String str) { - Assertions.assertEquals(d, Double.parseDouble(str), 0.0); - } - - private static void assertParsedDouble(final ParsedDouble parsed, final boolean negative, final String digits, - final int exponent) { - Assertions.assertEquals(negative, parsed.isNegative()); - Assertions.assertEquals(digits, parsed.getDigits()); - Assertions.assertEquals(exponent, parsed.getExponent()); - Assertions.assertEquals(digits.length(), parsed.getPrecision()); - Assertions.assertEquals(exponent, parsed.getScientificExponent() - digits.length() + 1); - } - - private static double createRandomDouble(final UniformRandomProvider rng) { - final long mask = ((1L << 52) - 1) | 1L << 63; - final long bits = rng.nextLong() & mask; - final long exp = rng.nextInt(2045) + 1; - return Double.longBitsToDouble(bits | (exp << 52)); - } -} diff --git a/commons-geometry-io-euclidean/src/main/java/org/apache/commons/geometry/io/euclidean/threed/obj/ObjBoundaryWriteHandler3D.java b/commons-geometry-io-euclidean/src/main/java/org/apache/commons/geometry/io/euclidean/threed/obj/ObjBoundaryWriteHandler3D.java index 0d7218f..b9d3f60 100644 --- a/commons-geometry-io-euclidean/src/main/java/org/apache/commons/geometry/io/euclidean/threed/obj/ObjBoundaryWriteHandler3D.java +++ b/commons-geometry-io-euclidean/src/main/java/org/apache/commons/geometry/io/euclidean/threed/obj/ObjBoundaryWriteHandler3D.java @@ -29,7 +29,6 @@ import org.apache.commons.geometry.euclidean.threed.PlaneConvexSubset; import org.apache.commons.geometry.euclidean.threed.mesh.Mesh; import org.apache.commons.geometry.io.core.GeometryFormat; import org.apache.commons.geometry.io.core.output.GeometryOutput; -import org.apache.commons.geometry.io.core.utils.DoubleFormats; import org.apache.commons.geometry.io.euclidean.threed.AbstractBoundaryWriteHandler3D; import org.apache.commons.geometry.io.euclidean.threed.FacetDefinition; import org.apache.commons.geometry.io.euclidean.threed.GeometryFormat3D; @@ -52,7 +51,7 @@ public class ObjBoundaryWriteHandler3D extends AbstractBoundaryWriteHandler3D { private String lineSeparator = DEFAULT_LINE_SEPARATOR; /** Double format function. */ - private DoubleFunction<String> doubleFormat = DoubleFormats.DOUBLE_TO_STRING; + private DoubleFunction<String> doubleFormat = Double::toString; /** Batch size used for mesh buffer creation. */ private int meshBufferBatchSize = DEFAULT_MESH_BUFFER_BATCH_SIZE; diff --git a/commons-geometry-io-euclidean/src/main/java/org/apache/commons/geometry/io/euclidean/threed/txt/AbstractTextBoundaryWriteHandler3D.java b/commons-geometry-io-euclidean/src/main/java/org/apache/commons/geometry/io/euclidean/threed/txt/AbstractTextBoundaryWriteHandler3D.java index a3f424b..58408b4 100644 --- a/commons-geometry-io-euclidean/src/main/java/org/apache/commons/geometry/io/euclidean/threed/txt/AbstractTextBoundaryWriteHandler3D.java +++ b/commons-geometry-io-euclidean/src/main/java/org/apache/commons/geometry/io/euclidean/threed/txt/AbstractTextBoundaryWriteHandler3D.java @@ -27,7 +27,6 @@ import java.util.stream.Stream; import org.apache.commons.geometry.euclidean.threed.PlaneConvexSubset; import org.apache.commons.geometry.io.core.output.GeometryOutput; -import org.apache.commons.geometry.io.core.utils.DoubleFormats; import org.apache.commons.geometry.io.euclidean.threed.AbstractBoundaryWriteHandler3D; import org.apache.commons.geometry.io.euclidean.threed.FacetDefinition; @@ -47,7 +46,7 @@ public abstract class AbstractTextBoundaryWriteHandler3D extends AbstractBoundar private String lineSeparator = DEFAULT_LINE_SEPARATOR; /** Double format function. */ - private DoubleFunction<String> doubleFormat = DoubleFormats.DOUBLE_TO_STRING; + private DoubleFunction<String> doubleFormat = Double::toString; /** Get the text output default charset, used if the output does not * specify a charset. diff --git a/commons-geometry-io-euclidean/src/test/java/org/apache/commons/geometry/io/euclidean/threed/obj/ObjBoundaryWriteHandler3DTest.java b/commons-geometry-io-euclidean/src/test/java/org/apache/commons/geometry/io/euclidean/threed/obj/ObjBoundaryWriteHandler3DTest.java index 2c54d7e..b3b474f 100644 --- a/commons-geometry-io-euclidean/src/test/java/org/apache/commons/geometry/io/euclidean/threed/obj/ObjBoundaryWriteHandler3DTest.java +++ b/commons-geometry-io-euclidean/src/test/java/org/apache/commons/geometry/io/euclidean/threed/obj/ObjBoundaryWriteHandler3DTest.java @@ -19,15 +19,17 @@ package org.apache.commons.geometry.io.euclidean.threed.obj; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; import java.util.Arrays; import java.util.List; +import java.util.Locale; import java.util.stream.Collectors; import org.apache.commons.geometry.euclidean.threed.BoundarySource3D; import org.apache.commons.geometry.euclidean.threed.Vector3D; import org.apache.commons.geometry.euclidean.threed.mesh.SimpleTriangleMesh; import org.apache.commons.geometry.io.core.output.StreamGeometryOutput; -import org.apache.commons.geometry.io.core.utils.DoubleFormats; import org.apache.commons.geometry.io.euclidean.threed.FacetDefinition; import org.apache.commons.geometry.io.euclidean.threed.FacetDefinitions; import org.apache.commons.geometry.io.euclidean.threed.GeometryFormat3D; @@ -57,14 +59,18 @@ class ObjBoundaryWriteHandler3DTest { Assertions.assertEquals(GeometryFormat3D.OBJ, handler.getFormat()); Assertions.assertEquals(StandardCharsets.UTF_8, handler.getDefaultCharset()); Assertions.assertEquals("\n", handler.getLineSeparator()); - Assertions.assertSame(DoubleFormats.DOUBLE_TO_STRING, handler.getDoubleFormat()); + Assertions.assertNotNull(handler.getDoubleFormat()); Assertions.assertEquals(-1, handler.getMeshBufferBatchSize()); } @Test void testWriteFacets() throws IOException { + // arrange + final DecimalFormat fmt = + new DecimalFormat("0.0#####", DecimalFormatSymbols.getInstance(Locale.ENGLISH)); + // act - handler.setDoubleFormat(DoubleFormats.createDefault(0, -6)); + handler.setDoubleFormat(fmt::format); handler.writeFacets(FACETS, new StreamGeometryOutput(out)); // assert @@ -80,8 +86,12 @@ class ObjBoundaryWriteHandler3DTest { @Test void testWriteFacets_usesOutputCharset() throws IOException { + // arrange + final DecimalFormat fmt = + new DecimalFormat("0.0#####", DecimalFormatSymbols.getInstance(Locale.ENGLISH)); + // act - handler.setDoubleFormat(DoubleFormats.createDefault(0, -6)); + handler.setDoubleFormat(fmt::format); handler.writeFacets(FACETS, new StreamGeometryOutput(out, null, StandardCharsets.UTF_16)); // assert @@ -98,9 +108,13 @@ class ObjBoundaryWriteHandler3DTest { @Test void testWriteFacets_customConfig() throws IOException { // arrange + // arrange + final DecimalFormat fmt = + new DecimalFormat("0.0", DecimalFormatSymbols.getInstance(Locale.ENGLISH)); + handler.setDefaultCharset(StandardCharsets.UTF_16); handler.setLineSeparator("\r\n"); - handler.setDoubleFormat(DoubleFormats.createDefault(0, -1)); + handler.setDoubleFormat(fmt::format); handler.setMeshBufferBatchSize(1); // act @@ -147,9 +161,13 @@ class ObjBoundaryWriteHandler3DTest { .map(f -> FacetDefinitions.toPolygon(f, TEST_PRECISION)) .collect(Collectors.toList())); + // arrange + final DecimalFormat fmt = + new DecimalFormat("0.0", DecimalFormatSymbols.getInstance(Locale.ENGLISH)); + handler.setDefaultCharset(StandardCharsets.UTF_16); handler.setLineSeparator("\r\n"); - handler.setDoubleFormat(DoubleFormats.createDefault(0, -1)); + handler.setDoubleFormat(fmt::format); handler.setMeshBufferBatchSize(1); // act diff --git a/commons-geometry-io-euclidean/src/test/java/org/apache/commons/geometry/io/euclidean/threed/obj/ObjWriterTest.java b/commons-geometry-io-euclidean/src/test/java/org/apache/commons/geometry/io/euclidean/threed/obj/ObjWriterTest.java index 5e32a16..61d58cc 100644 --- a/commons-geometry-io-euclidean/src/test/java/org/apache/commons/geometry/io/euclidean/threed/obj/ObjWriterTest.java +++ b/commons-geometry-io-euclidean/src/test/java/org/apache/commons/geometry/io/euclidean/threed/obj/ObjWriterTest.java @@ -19,7 +19,10 @@ package org.apache.commons.geometry.io.euclidean.threed.obj; import java.io.IOException; import java.io.StringWriter; import java.io.UncheckedIOException; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; import java.util.Arrays; +import java.util.Locale; import java.util.regex.Pattern; import org.apache.commons.geometry.core.GeometryTestUtils; @@ -27,7 +30,6 @@ import org.apache.commons.geometry.euclidean.threed.BoundarySource3D; import org.apache.commons.geometry.euclidean.threed.Planes; import org.apache.commons.geometry.euclidean.threed.Vector3D; import org.apache.commons.geometry.euclidean.threed.mesh.SimpleTriangleMesh; -import org.apache.commons.geometry.io.core.utils.DoubleFormats; import org.apache.commons.geometry.io.euclidean.threed.SimpleFacetDefinition; import org.apache.commons.numbers.core.Precision; import org.junit.jupiter.api.Assertions; @@ -48,7 +50,7 @@ class ObjWriterTest { // act/assert try (ObjWriter objWriter = new ObjWriter(writer)) { Assertions.assertEquals("\n", objWriter.getLineSeparator()); - Assertions.assertSame(DoubleFormats.DOUBLE_TO_STRING, objWriter.getDoubleFormat()); + Assertions.assertNotNull(objWriter.getDoubleFormat()); Assertions.assertEquals(0, objWriter.getVertexCount()); Assertions.assertEquals(0, objWriter.getVertexNormalCount()); } @@ -92,10 +94,12 @@ class ObjWriterTest { void testSetDecimalFormat() throws IOException { // arrange final StringWriter writer = new StringWriter(); + final DecimalFormat fmt = + new DecimalFormat("0.0", DecimalFormatSymbols.getInstance(Locale.ENGLISH)); // act try (ObjWriter objWriter = new ObjWriter(writer)) { - objWriter.setDoubleFormat(DoubleFormats.createDefault(0, -1)); + objWriter.setDoubleFormat(fmt::format); objWriter.writeVertex(Vector3D.of(1.09, 2.05, 3.06)); } @@ -156,12 +160,16 @@ class ObjWriterTest { // arrange final StringWriter writer = new StringWriter(); + // arrange + final DecimalFormat fmt = + new DecimalFormat("0.0", DecimalFormatSymbols.getInstance(Locale.ENGLISH)); + // act final int index1; final int index2; final int count; try (ObjWriter objWriter = new ObjWriter(writer)) { - objWriter.setDoubleFormat(DoubleFormats.createDefault(0, -1)); + objWriter.setDoubleFormat(fmt::format); index1 = objWriter.writeVertex(Vector3D.of(1.09, 2.1, 3.005)); index2 = objWriter.writeVertex(Vector3D.of(0.06, 10, 12)); @@ -182,13 +190,15 @@ class ObjWriterTest { void testWriteNormal() throws IOException { // arrange final StringWriter writer = new StringWriter(); + final DecimalFormat fmt = + new DecimalFormat("0.0", DecimalFormatSymbols.getInstance(Locale.ENGLISH)); // act final int index1; final int index2; final int count; try (ObjWriter objWriter = new ObjWriter(writer)) { - objWriter.setDoubleFormat(DoubleFormats.createDefault(0, -1)); + objWriter.setDoubleFormat(fmt::format); index1 = objWriter.writeVertexNormal(Vector3D.of(1.09, 2.1, 3.005)); index2 = objWriter.writeVertexNormal(Vector3D.of(0.06, 10, 12)); diff --git a/commons-geometry-io-euclidean/src/test/java/org/apache/commons/geometry/io/euclidean/threed/stl/TextStlWriterTest.java b/commons-geometry-io-euclidean/src/test/java/org/apache/commons/geometry/io/euclidean/threed/stl/TextStlWriterTest.java index 4f9eba3..8cf53d9 100644 --- a/commons-geometry-io-euclidean/src/test/java/org/apache/commons/geometry/io/euclidean/threed/stl/TextStlWriterTest.java +++ b/commons-geometry-io-euclidean/src/test/java/org/apache/commons/geometry/io/euclidean/threed/stl/TextStlWriterTest.java @@ -18,16 +18,18 @@ package org.apache.commons.geometry.io.euclidean.threed.stl; import java.io.IOException; import java.io.StringWriter; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Locale; import org.apache.commons.geometry.core.GeometryTestUtils; import org.apache.commons.geometry.euclidean.threed.ConvexPolygon3D; import org.apache.commons.geometry.euclidean.threed.Planes; import org.apache.commons.geometry.euclidean.threed.Vector3D; import org.apache.commons.geometry.io.core.test.CloseCountWriter; -import org.apache.commons.geometry.io.core.utils.DoubleFormats; import org.apache.commons.geometry.io.euclidean.threed.FacetDefinition; import org.apache.commons.geometry.io.euclidean.threed.SimpleFacetDefinition; import org.apache.commons.numbers.core.Precision; @@ -47,7 +49,7 @@ class TextStlWriterTest { void testDefaultProperties() throws IOException { // act/assert try (TextStlWriter writer = new TextStlWriter(out)) { - Assertions.assertSame(DoubleFormats.DOUBLE_TO_STRING, writer.getDoubleFormat()); + Assertions.assertNotNull(writer.getDoubleFormat()); Assertions.assertEquals("\n", writer.getLineSeparator()); } } @@ -540,9 +542,12 @@ class TextStlWriterTest { Vector3D.ZERO, Vector3D.of(1.0 / 3.0, 0, 0), Vector3D.of(0, 1.0 / 3.0, 0)); final Vector3D normal = Vector3D.Unit.PLUS_Z; + final DecimalFormat fmt = + new DecimalFormat("0.0##", DecimalFormatSymbols.getInstance(Locale.ENGLISH)); + try (TextStlWriter writer = new TextStlWriter(out)) { - writer.setDoubleFormat(DoubleFormats.createDefault(0, -3)); + writer.setDoubleFormat(fmt::format); writer.setLineSeparator("\r\n"); // act diff --git a/commons-geometry-io-euclidean/src/test/java/org/apache/commons/geometry/io/euclidean/threed/txt/TextBoundaryWriteHandler3DTest.java b/commons-geometry-io-euclidean/src/test/java/org/apache/commons/geometry/io/euclidean/threed/txt/TextBoundaryWriteHandler3DTest.java index 575e39f..8b6e21f 100644 --- a/commons-geometry-io-euclidean/src/test/java/org/apache/commons/geometry/io/euclidean/threed/txt/TextBoundaryWriteHandler3DTest.java +++ b/commons-geometry-io-euclidean/src/test/java/org/apache/commons/geometry/io/euclidean/threed/txt/TextBoundaryWriteHandler3DTest.java @@ -19,15 +19,17 @@ package org.apache.commons.geometry.io.euclidean.threed.txt; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; import java.util.Arrays; import java.util.List; +import java.util.Locale; import org.apache.commons.geometry.euclidean.threed.BoundarySource3D; import org.apache.commons.geometry.euclidean.threed.Planes; import org.apache.commons.geometry.euclidean.threed.Vector3D; import org.apache.commons.geometry.io.core.output.StreamGeometryOutput; import org.apache.commons.geometry.io.core.test.CloseCountOutputStream; -import org.apache.commons.geometry.io.core.utils.DoubleFormats; import org.apache.commons.geometry.io.euclidean.threed.FacetDefinition; import org.apache.commons.geometry.io.euclidean.threed.GeometryFormat3D; import org.apache.commons.geometry.io.euclidean.threed.SimpleFacetDefinition; @@ -66,7 +68,7 @@ class TextBoundaryWriteHandler3DTest { Assertions.assertEquals("\n", handler.getLineSeparator()); Assertions.assertEquals(" ", handler.getVertexComponentSeparator()); Assertions.assertEquals("; ", handler.getVertexSeparator()); - Assertions.assertSame(DoubleFormats.DOUBLE_TO_STRING, handler.getDoubleFormat()); + Assertions.assertNotNull(handler.getDoubleFormat()); Assertions.assertEquals(-1, handler.getFacetVertexCount()); } @@ -103,10 +105,13 @@ class TextBoundaryWriteHandler3DTest { @Test void testWriteFacets_customConfiguration() throws IOException { // arrange + final DecimalFormat fmt = + new DecimalFormat("0.0", DecimalFormatSymbols.getInstance(Locale.ENGLISH)); + final TextBoundaryWriteHandler3D handler = new TextBoundaryWriteHandler3D(); handler.setDefaultCharset(StandardCharsets.UTF_16); handler.setLineSeparator("\r\n"); - handler.setDoubleFormat(DoubleFormats.createDefault(0, -1)); + handler.setDoubleFormat(fmt::format); handler.setVertexComponentSeparator("|"); handler.setVertexSeparator(" | "); handler.setFacetVertexCount(4); @@ -140,10 +145,14 @@ class TextBoundaryWriteHandler3DTest { @Test void testWriteBoundarySource_customConfiguration() throws IOException { // arrange + // arrange + final DecimalFormat fmt = + new DecimalFormat("0.0", DecimalFormatSymbols.getInstance(Locale.ENGLISH)); + final TextBoundaryWriteHandler3D handler = new TextBoundaryWriteHandler3D(); handler.setDefaultCharset(StandardCharsets.UTF_16); handler.setLineSeparator("\r\n"); - handler.setDoubleFormat(DoubleFormats.createDefault(0, -1)); + handler.setDoubleFormat(fmt::format); handler.setVertexComponentSeparator("|"); handler.setVertexSeparator(" | "); handler.setFacetVertexCount(4); diff --git a/commons-geometry-io-euclidean/src/test/java/org/apache/commons/geometry/io/euclidean/threed/txt/TextFacetDefinitionWriterTest.java b/commons-geometry-io-euclidean/src/test/java/org/apache/commons/geometry/io/euclidean/threed/txt/TextFacetDefinitionWriterTest.java index 9329e37..8d0b516 100644 --- a/commons-geometry-io-euclidean/src/test/java/org/apache/commons/geometry/io/euclidean/threed/txt/TextFacetDefinitionWriterTest.java +++ b/commons-geometry-io-euclidean/src/test/java/org/apache/commons/geometry/io/euclidean/threed/txt/TextFacetDefinitionWriterTest.java @@ -18,9 +18,12 @@ package org.apache.commons.geometry.io.euclidean.threed.txt; import java.io.IOException; import java.io.StringWriter; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Locale; import org.apache.commons.geometry.core.GeometryTestUtils; import org.apache.commons.geometry.euclidean.threed.BoundarySource3D; @@ -28,7 +31,6 @@ import org.apache.commons.geometry.euclidean.threed.ConvexPolygon3D; import org.apache.commons.geometry.euclidean.threed.PlaneConvexSubset; import org.apache.commons.geometry.euclidean.threed.Planes; import org.apache.commons.geometry.euclidean.threed.Vector3D; -import org.apache.commons.geometry.io.core.utils.DoubleFormats; import org.apache.commons.geometry.io.euclidean.threed.SimpleFacetDefinition; import org.apache.commons.numbers.core.Precision; import org.junit.jupiter.api.Assertions; @@ -56,7 +58,7 @@ class TextFacetDefinitionWriterTest { void testPropertyDefaults() { // act/assert Assertions.assertEquals("\n", fdWriter.getLineSeparator()); - Assertions.assertSame(DoubleFormats.DOUBLE_TO_STRING, fdWriter.getDoubleFormat()); + Assertions.assertNotNull(fdWriter.getDoubleFormat()); Assertions.assertEquals(" ", fdWriter.getVertexComponentSeparator()); Assertions.assertEquals("; ", fdWriter.getVertexSeparator()); Assertions.assertEquals(-1, fdWriter.getFacetVertexCount()); @@ -188,12 +190,15 @@ class TextFacetDefinitionWriterTest { @Test void testWriteFacetDefinition() throws IOException { // arrange + final DecimalFormat fmt = + new DecimalFormat("0.0##", DecimalFormatSymbols.getInstance(Locale.ENGLISH)); + final SimpleFacetDefinition f1 = new SimpleFacetDefinition(Arrays.asList( Vector3D.ZERO, Vector3D.of(0.5, 0, 0), Vector3D.of(0, -0.5, 0))); final SimpleFacetDefinition f2 = new SimpleFacetDefinition(Arrays.asList( Vector3D.of(0.5, 0.7, 1.2), Vector3D.of(10.01, -4, 2), Vector3D.of(-10.0 / 3.0, 0, 0), Vector3D.ZERO)); - fdWriter.setDoubleFormat(DoubleFormats.createDefault(0, -3)); + fdWriter.setDoubleFormat(fmt::format); // act fdWriter.write(f1); @@ -299,6 +304,8 @@ class TextFacetDefinitionWriterTest { @Test void testWriteBoundarySource_alternativeFormatting() throws IOException { // arrange + final DecimalFormat fmt = + new DecimalFormat("0.0", DecimalFormatSymbols.getInstance(Locale.ENGLISH)); final ConvexPolygon3D poly1 = Planes.convexPolygonFromVertices(Arrays.asList( Vector3D.ZERO, Vector3D.of(0, 0, -0.5901), Vector3D.of(0, -0.501, 0) ), TEST_PRECISION); @@ -306,7 +313,7 @@ class TextFacetDefinitionWriterTest { Vector3D.ZERO, Vector3D.of(1, 0, 0), Vector3D.of(1, 1, 0), Vector3D.of(0, 1, 0) ), TEST_PRECISION); - fdWriter.setDoubleFormat(DoubleFormats.createDefault(0, -1)); + fdWriter.setDoubleFormat(fmt::format); fdWriter.setFacetVertexCount(3); fdWriter.setLineSeparator("\r\n");
