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 de9772b9665a082fdc2851688cedea6dd4f1e694
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Fri Nov 30 16:52:21 2018 +0100
MathFunctions.toNanFloat(int) should always map to quiet NaN. Previous version was mapping
to signaling NaN when the given ordinal was negative.
---
.../java/org/apache/sis/math/MathFunctions.java | 53 +++++++++++++++++-----
.../org/apache/sis/math/MathFunctionsTest.java | 25 ++++++----
2 files changed, 57 insertions(+), 21 deletions(-)
diff --git a/core/sis-utility/src/main/java/org/apache/sis/math/MathFunctions.java b/core/sis-utility/src/main/java/org/apache/sis/math/MathFunctions.java
index 51c49cd..6f07da8 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/math/MathFunctions.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/math/MathFunctions.java
@@ -57,7 +57,7 @@ import static org.apache.sis.internal.util.Numerics.SIGNIFICAND_SIZE;
*
* @author Martin Desruisseaux (MPO, IRD, Geomatys)
* @author Johann Sorel (Geomatys)
- * @version 0.7
+ * @version 1.0
*
* @see DecimalFunctions
* @see org.apache.sis.util.Numbers
@@ -89,20 +89,44 @@ public final class MathFunctions extends Static {
public static final double LOG10_2 = 0.3010299956639812;
/**
- * The minimal ordinal value for {@code NaN} numbers created by {@link #toNanFloat(int)}.
+ * The minimal and maximal ordinal value for {@code NaN} numbers created by {@link #toNanFloat(int)}.
+ * The range is selected in way to restrict ourselves to <cite>quiet</cite>
NaN values.
+ * The following is an adaptation of evaluator's comments for bug #4471414
+ * (http://developer.java.sun.com/developer/bugParade/bugs/4471414.html):
+ *
+ * <blockquote>
+ * There are actually two types of NaNs, signaling NaNs and quiet NaNs. Java doesn't
support the features necessary
+ * to reliably distinguish the two. However, the relevant point is that copying a
signaling NaN may (or may not, at
+ * the implementors discretion) yield a quiet NaN — a NaN with a different bit pattern
(IEEE 754 6.2). Therefore, on
+ * IEEE 754 compliant platforms it may be impossible to find a signaling NaN stored
in an array since a signaling NaN
+ * passed as an argument to binarySearch may get replaced by a quiet NaN.
+ * </blockquote>
+ *
+ * The relevant thresholds are:
+ * <ul>
+ * <li>{@code 7F800000}: positive infinity.</li>
+ * <li>{@code 7F800001}: first signaling NaN in the range of positive values.</li>
+ * <li>{@code 7FBFFFFF}: last signaling NaN.</li>
+ * <li>{@code 7FC00000}: first quiet NaN. Also the standard {@link Double#NaN}
value.</li>
+ * <li>{@code 7FFFFFFF}: last quiet NaN.</li>
+ * <li>{@code FF800000}: negative infinity.</li>
+ * <li>{@code FF800001}: first signaling NaN in the range of negative values.</li>
+ * <li>{@code FFBFFFFF}: last signaling NaN.</li>
+ * <li>{@code FFC00000}: first quiet NaN in the range of negative values.</li>
+ * <li>{@code FFFFFFFF}: last quiet NaN.</li>
+ * </ul>
*
* @see #toNanFloat(int)
* @see #toNanOrdinal(float)
*/
- private static final int MIN_NAN_ORDINAL = -0x200000;
+ static final int MIN_NAN_ORDINAL = -0x200000,
+ MAX_NAN_ORDINAL = 0x1FFFFF;
/**
- * The maximal ordinal value for {@code NaN} numbers created by {@link #toNanFloat(int)}.
- *
- * @see #toNanFloat(int)
- * @see #toNanOrdinal(float)
+ * The beginning of ranges of quiet NaN values.
*/
- static final int MAX_NAN_ORDINAL = 0x1FFFFF;
+ static final int POSITIVE_NAN = 0x7FC00000,
+ NEGATIVE_NAN = 0xFFC00000;
/**
* The highest prime number supported by the {@link #nextPrimeNumber(int)} method.
@@ -572,7 +596,10 @@ public final class MathFunctions extends Static {
*/
public static float toNanFloat(final int ordinal) throws IllegalArgumentException {
ArgumentChecks.ensureBetween("ordinal", MIN_NAN_ORDINAL, MAX_NAN_ORDINAL, ordinal);
- final float value = intBitsToFloat(0x7FC00000 + ordinal);
+ int bits = (ordinal >= 0) ? ordinal : ~ordinal;
+ bits = (bits + POSITIVE_NAN) | (ordinal & Integer.MIN_VALUE);
+ assert Integer.compareUnsigned(bits, ordinal >= 0 ? POSITIVE_NAN : NEGATIVE_NAN)
>= 0 : ordinal;
+ final float value = intBitsToFloat(bits);
assert Float.isNaN(value) && toNanOrdinal(value) == ordinal : ordinal;
return value;
}
@@ -591,7 +618,9 @@ public final class MathFunctions extends Static {
* or does not use a supported bits pattern.
*/
public static int toNanOrdinal(final float value) throws IllegalArgumentException {
- final int ordinal = floatToRawIntBits(value) - 0x7FC00000;
+ final int bits = floatToRawIntBits(value);
+ int ordinal = (bits & Integer.MAX_VALUE) - POSITIVE_NAN;
+ if (bits < 0) ordinal = ~ordinal;
if (ordinal >= MIN_NAN_ORDINAL && ordinal <= MAX_NAN_ORDINAL) {
return ordinal;
}
@@ -599,10 +628,10 @@ public final class MathFunctions extends Static {
final Object obj;
if (Float.isNaN(value)) {
resourceKey = Errors.Keys.IllegalBitsPattern_1;
- obj = Integer.toHexString(ordinal);
+ obj = Integer.toHexString(bits);
} else {
resourceKey = Errors.Keys.IllegalArgumentValue_2;
- obj = value;
+ obj = new Object[] {"value", value};
}
throw new IllegalArgumentException(Errors.format(resourceKey, obj));
}
diff --git a/core/sis-utility/src/test/java/org/apache/sis/math/MathFunctionsTest.java b/core/sis-utility/src/test/java/org/apache/sis/math/MathFunctionsTest.java
index 83a9a3d..cb0664a 100644
--- a/core/sis-utility/src/test/java/org/apache/sis/math/MathFunctionsTest.java
+++ b/core/sis-utility/src/test/java/org/apache/sis/math/MathFunctionsTest.java
@@ -33,7 +33,7 @@ import static org.apache.sis.internal.util.Numerics.SIGNIFICAND_SIZE;
*
* @author Martin Desruisseaux (IRD, Geomatys)
* @author Johann Sorel (Geomatys)
- * @version 0.8
+ * @version 1.0
* @since 0.3
* @module
*/
@@ -246,14 +246,21 @@ public final strictfp class MathFunctionsTest extends TestCase {
@Test
public void testToNanFloat() {
final int standardNaN = Float.floatToRawIntBits(Float.NaN);
- for (int ordinal = 0; ordinal < MathFunctions.MAX_NAN_ORDINAL; ordinal += 256)
{
- final float vp = toNanFloat(+ordinal);
- final float vn = toNanFloat(-ordinal);
- final int bp = Float.floatToRawIntBits(vp);
- final int bn = Float.floatToRawIntBits(vn);
- assertEquals(ordinal == 0, standardNaN == bp);
- assertEquals(ordinal == 0, standardNaN == bn);
- assertEquals(ordinal == 0, bp == bn);
+ for (int ordinal = MathFunctions.MIN_NAN_ORDINAL;
+ ordinal < MathFunctions.MAX_NAN_ORDINAL;
+ ordinal += 256)
+ {
+ final float value = toNanFloat(ordinal);
+ final int bits = Float.floatToRawIntBits(value);
+ if (ordinal >= 0) {
+ assertTrue(Integer.compareUnsigned(bits, MathFunctions.POSITIVE_NAN) >=
0);
+ assertTrue(Integer.compareUnsigned(bits, 0x7FFFFFFF) <= 0);
+ } else {
+ assertTrue(Integer.compareUnsigned(bits, MathFunctions.NEGATIVE_NAN) >=
0);
+ assertTrue(Integer.compareUnsigned(bits, 0xFFFFFFFF) <= 0);
+ }
+ assertEquals(ordinal == 0, bits == standardNaN);
+ assertEquals(ordinal, toNanOrdinal(value));
}
}
|