sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] 01/02: MathFunctions.toNanFloat(int) should always map to quiet NaN. Previous version was mapping to signaling NaN when the given ordinal was negative.
Date Fri, 30 Nov 2018 15:54:43 GMT
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));
         }
     }
 


Mime
View raw message