This is an automated email from the ASF dual-hosted git repository. desruisseaux pushed a commit to branch geoapi-4.0 in repository https://gitbox.apache.org/repos/asf/sis.git
commit 887a01a31010a04d34188f397956d9972a7fdf9c Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Thu Oct 28 12:53:19 2021 +0200 Allow to restrict the "best fit range" to floating point types. It makes a difference in the interpretation of next value after the maximum value. --- .../java/org/apache/sis/measure/NumberRange.java | 61 +++++++++++++++++++--- .../java/org/apache/sis/measure/package-info.java | 2 +- .../org/apache/sis/measure/NumberRangeTest.java | 4 +- 3 files changed, 58 insertions(+), 9 deletions(-) diff --git a/core/sis-utility/src/main/java/org/apache/sis/measure/NumberRange.java b/core/sis-utility/src/main/java/org/apache/sis/measure/NumberRange.java index dd16d34..99cce2d 100644 --- a/core/sis-utility/src/main/java/org/apache/sis/measure/NumberRange.java +++ b/core/sis-utility/src/main/java/org/apache/sis/measure/NumberRange.java @@ -80,7 +80,7 @@ import org.apache.sis.util.collection.WeakHashSet; * * @author Martin Desruisseaux (IRD, Geomatys) * @author Jody Garnett (for parameterized type inspiration) - * @version 1.1 + * @version 1.2 * * @param <E> the type of range elements as a subclass of {@link Number}. * @@ -321,7 +321,8 @@ public class NumberRange<E extends Number & Comparable<? super E>> extends Range * {@value java.lang.Short#MIN_VALUE} and {@value java.lang.Short#MAX_VALUE} inclusive.</li> * <li>{@code NumberRange<Integer>} if the given values are integers between * {@value java.lang.Integer#MIN_VALUE} and {@value java.lang.Integer#MAX_VALUE} inclusive.</li> - * <li>{@code NumberRange<Long>} if the given values are integers in the range of {@code long} values.</li> + * <li>{@code NumberRange<Long>} if the given values are integers between + * {@value java.lang.Long#MIN_VALUE} and {@value java.lang.Long#MAX_VALUE} inclusive.</li> * <li>{@code NumberRange<Float>} if the given values can be casted to {@code float} values without data lost.</li> * <li>{@code NumberRange<Double>} if none of the above types is suitable.</li> * </ul> @@ -335,15 +336,53 @@ public class NumberRange<E extends Number & Comparable<? super E>> extends Range * @return the new range, or {@code null} if both {@code minValue} and {@code maxValue} are {@code null}. * @throws IllegalArgumentException if the given numbers are not primitive wrappers for numeric types. */ - @SuppressWarnings({"rawtypes","unchecked"}) public static NumberRange<?> createBestFit(final Number minValue, final boolean isMinIncluded, final Number maxValue, final boolean isMaxIncluded) { + return createBestFit(false, minValue, isMinIncluded, maxValue, isMaxIncluded); + } + + /** + * Constructs a range using the smallest integer type or floating point type that can hold the given values. + * If {@code asFloat} is {@code false}, then the returned range can use any wrapper type and this method behaves + * as described in {@linkplain #createBestFit(java.lang.Number, boolean, java.lang.Number, boolean) above method}. + * If {@code asFloat} is {@code true}, then the returned range is restricted to {@link Float} and {@link Double} + * number types; integer types are casted to one of the floating point types. + * + * @param asFloat whether to restrict the returned range to floating point types. + * @param minValue the minimal value, or {@code null} if none. + * @param isMinIncluded {@code true} if the minimal value is inclusive, or {@code false} if exclusive. + * @param maxValue the maximal value, or {@code null} if none. + * @param isMaxIncluded {@code true} if the maximal value is inclusive, or {@code false} if exclusive. + * @return the new range, or {@code null} if both {@code minValue} and {@code maxValue} are {@code null}. + * @throws IllegalArgumentException if the given numbers are not primitive wrappers for numeric types. + * + * @since 1.2 + */ + @SuppressWarnings({"rawtypes","unchecked"}) + public static NumberRange<?> createBestFit(final boolean asFloat, + final Number minValue, final boolean isMinIncluded, + final Number maxValue, final boolean isMaxIncluded) + { final Class<? extends Number> type; - type = Numbers.widestClass(Numbers.narrowestClass(minValue), - Numbers.narrowestClass(maxValue)); - if (type == null) { - return null; + if (asFloat) { + if (minValue == null && maxValue == null) { + return null; + } + // Types supported below should be consistent with the comment in next block. + type = (isFloat(minValue) && isFloat(maxValue)) ? Float.class : Double.class; + } else { + /* + * The `narrowestClass(…)` method currently returns only wrappers of primitive types. + * The `Fraction`, `BigInteger` or `BigDecimal` types accepted by `widestClass(…)` are lost. + * We could support those additional classes as well (by improving `narrowestClass(…)` and + * updating above Javadoc), but we do not yet have a need for them. + */ + type = Numbers.widestClass(Numbers.narrowestClass(minValue), + Numbers.narrowestClass(maxValue)); + if (type == null) { + return null; + } } Number min = Numbers.cast(minValue, type); Number max = Numbers.cast(maxValue, type); @@ -359,6 +398,14 @@ public class NumberRange<E extends Number & Comparable<? super E>> extends Range } /** + * Returns {@code true} if the given value can be casted to the {@code Float} type. + */ + private static boolean isFloat(final Number value) { + return (value == null) || + Double.doubleToRawLongBits(value.floatValue()) == Double.doubleToRawLongBits(value.doubleValue()); + } + + /** * Constructs a range of {@code int} values without upper bound. * This method may return a shared instance, at implementation choice. * diff --git a/core/sis-utility/src/main/java/org/apache/sis/measure/package-info.java b/core/sis-utility/src/main/java/org/apache/sis/measure/package-info.java index 3f2a291..f6f01c1 100644 --- a/core/sis-utility/src/main/java/org/apache/sis/measure/package-info.java +++ b/core/sis-utility/src/main/java/org/apache/sis/measure/package-info.java @@ -96,7 +96,7 @@ * are also recognized at parsing time.</p> * * @author Martin Desruisseaux (MPO, IRD, Geomatys) - * @version 1.1 + * @version 1.2 * @since 0.3 * @module */ diff --git a/core/sis-utility/src/test/java/org/apache/sis/measure/NumberRangeTest.java b/core/sis-utility/src/test/java/org/apache/sis/measure/NumberRangeTest.java index a85dca2..b5a35e4 100644 --- a/core/sis-utility/src/test/java/org/apache/sis/measure/NumberRangeTest.java +++ b/core/sis-utility/src/test/java/org/apache/sis/measure/NumberRangeTest.java @@ -28,7 +28,7 @@ import static org.junit.Assert.*; * Tests the {@link NumberRange} class. * * @author Martin Desruisseaux (IRD, Geomatys) - * @version 1.1 + * @version 1.2 * @since 0.3 * @module */ @@ -136,6 +136,8 @@ public final strictfp class NumberRangeTest extends TestCase { public void testCreateBestFit() { assertEquals(NumberRange.create((short) 2, true, (short) 200, true), NumberRange.createBestFit(2, true, 200.0, true)); + assertEquals(NumberRange.create((float) 2, true, (float) 200, true), + NumberRange.createBestFit(true, 2, true, 200.0, true)); } /**