sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1538039 - in /sis/branches/JDK7/core/sis-utility/src: main/java/org/apache/sis/internal/util/ main/java/org/apache/sis/math/ test/java/org/apache/sis/internal/util/ test/java/org/apache/sis/math/ test/java/org/apache/sis/test/suite/
Date Fri, 01 Nov 2013 19:16:37 GMT
Author: desruisseaux
Date: Fri Nov  1 19:16:36 2013
New Revision: 1538039

URL: http://svn.apache.org/r1538039
Log:
Numerical functions.

Modified:
    sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/util/DoubleDouble.java
    sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/util/Numerics.java
    sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/math/MathFunctions.java
    sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/internal/util/DoubleDoubleTest.java
    sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/internal/util/NumericsTest.java
    sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/math/MathFunctionsTest.java
    sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java

Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/util/DoubleDouble.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/util/DoubleDouble.java?rev=1538039&r1=1538038&r2=1538039&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/util/DoubleDouble.java
[UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/util/DoubleDouble.java
[UTF-8] Fri Nov  1 19:16:36 2013
@@ -129,9 +129,10 @@ public final class DoubleDouble extends 
         // digits for documentation purpose, and in order to have identical content than
DoubleDoubleTest
         // so that a plain copy-and-paste can be performed between those two classes.
          0.000001,
+         0.000004848136811095359935899141023579480, // Arc-second to radians
          0.00001,
          0.0001,
-         0.00027777777777777777777777777777777778,  // Second to degrees
+         0.0002777777777777777777777777777777778,   // Second to degrees
          0.001,
          0.002777777777777777777777777777777778,    // 1/360°
          0.01,
@@ -169,6 +170,7 @@ public final class DoubleDouble extends 
      */
     private static final double[] ERRORS = {
         /*  0.000001  */  4.525188817411374E-23,
+        /*  0.000004… */  9.320078015422868E-23,
         /*  0.00001   */ -8.180305391403131E-22,
         /*  0.0001    */ -4.79217360238593E-21,
         /*  0.000266… */  2.4093381610788987E-22,
@@ -235,6 +237,16 @@ public final class DoubleDouble extends 
         return new DoubleDouble(0.01745329251994329576923690768488613, 2.9486522708701687E-19);
     }
 
+    /**
+     * Returns a new {@code DoubleDouble} instance initialized to the conversion factor
+     * from arc-seconds to radians.
+     *
+     * @return An instance initialized to the 0.000004848136811095359935899141023579480 value.
+     */
+    public static DoubleDouble createSecondsToRadians() {
+        return new DoubleDouble(0.000004848136811095359935899141023579480, 9.320078015422868E-23);
+    }
+
     /** @return {@link #value}. */
     @Override public double doubleValue() {return value;}
     @Override public float  floatValue()  {return (float) value;}

Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/util/Numerics.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/util/Numerics.java?rev=1538039&r1=1538038&r2=1538039&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/util/Numerics.java
[UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/util/Numerics.java
[UTF-8] Fri Nov  1 19:16:36 2013
@@ -83,11 +83,10 @@ public final class Numerics extends Stat
     public static final int SIGNIFICAND_SIZE = 52;
 
     /**
-     * A prime number used for hash code computation. Value 31 is often used because
-     * some modern compilers can optimize {@code x*31} as {@code (x << 5) - x}
-     * (Josh Bloch, <cite>Effective Java</cite>).
+     * Number of bits in the significand (mantissa) part of IEEE 754 {@code float} representation,
+     * <strong>not</strong> including the hidden bit.
      */
-    private static final int PRIME_NUMBER = 31;
+    public static final int SIGNIFICAND_SIZE_OF_FLOAT = 23;
 
     /**
      * Do not allow instantiation of this class.
@@ -217,7 +216,12 @@ public final class Numerics extends Stat
      * @return An updated hash code value.
      */
     public static int hash(final float value, final int seed) {
-        return seed * PRIME_NUMBER + Float.floatToIntBits(value);
+        /*
+         * Multiplication by prime number produces better hash code distribution.
+         * Value 31 is often used because some modern compilers can optimize x*31
+         * as  (x << 5) - x    (Josh Bloch, Effective Java).
+         */
+        return 31*seed + Float.floatToIntBits(value);
     }
 
     /**
@@ -241,6 +245,98 @@ public final class Numerics extends Stat
      * @return An updated hash code value.
      */
     public static int hash(final long value, final int seed) {
-        return seed * PRIME_NUMBER + (((int) value) ^ ((int) (value >>> 32)));
+        return 31*seed + (((int) value) ^ ((int) (value >>> 32)));
+    }
+
+    /**
+     * Converts a power of 2 to a power of 10, rounded toward negative infinity.
+     * This method is equivalent to the following code, but using only integer arithmetic:
+     *
+     * {@preformat java
+     *     return (int) Math.floor(exp2 * LOG10_2);
+     * }
+     *
+     * This method is valid only for arguments in the [-2620 … 2620] range, which is more
than enough
+     * for the range of {@code double} exponents. We do not put this method in public API
because it
+     * does not check the argument validity.
+     *
+     * @param  exp2 The power of 2 to convert Must be in the [-2620 … 2620] range.
+     * @return The power of 10, rounded toward negative infinity.
+     *
+     * @see org.apache.sis.math.MathFunctions#LOG10_2
+     * @see org.apache.sis.math.MathFunctions#getExponent(double)
+     */
+    public static int toExp10(final int exp2) {
+        /*
+         * Compute:
+         *          exp2 × (log10(2) × 2ⁿ) / 2ⁿ
+         * where:
+         *          n = 20   (arbitrary value)
+         *
+         * log10(2) × 2ⁿ  =  315652.82873335475, which we round to 315653.
+         *
+         * The range of valid values for such approximation is determined
+         * empirically by running the NumericsTest.testToExp10() method.
+         */
+        assert exp2 >= -2620 && exp2 <= 2620 : exp2;
+        return (exp2 * 315653) >> 20;
+    }
+
+    /**
+     * Returns the significand <var>m</var> of the given value such as {@code
value = m×2ⁿ}
+     * where <var>n</var> is {@link Math#getExponent(double)} - {@value #SIGNIFICAND_SIZE}.
+     * For any non-NaN finite positive values, the following relationship should hold:
+     *
+     * {@preformat java
+     *    assert Math.scalb(getSignificand(value), Math.getExponent(value) - SIGNIFICAND_SIZE)
== value;
+     * }
+     *
+     * For negative values, this method behaves as if the value was positive.
+     * For NaN and infinite value, the returned value is undetermined.
+     *
+     * @param  value The value for which to get the significand.
+     * @return The significand of the given value.
+     */
+    public static long getSignificand(final double value) {
+        long bits = Double.doubleToRawLongBits(value);
+        final long exponent = bits & (0x7FFL << SIGNIFICAND_SIZE);
+        bits &= (1L << SIGNIFICAND_SIZE) - 1;
+        if (exponent != 0) {
+            bits |= (1L << SIGNIFICAND_SIZE);
+        } else {
+            /*
+             * Sub-normal value: compensate for the fact that Math.getExponent(value) returns
+             * Double.MIN_EXPONENT - 1 in this case, while we would need Double.MIN_EXPONENT.
+             */
+            bits <<= 1;
+        }
+        return bits;
+    }
+
+    /**
+     * Returns the significand <var>m</var> of the given value such as {@code
value = m×2ⁿ} where
+     * <var>n</var> is {@link Math#getExponent(float)} - {@value #SIGNIFICAND_SIZE_OF_FLOAT}.
+     * For any non-NaN finite positive values, the following relationship should hold:
+     *
+     * {@preformat java
+     *    assert Math.scalb(getSignificand(value), Math.getExponent(value) - SIGNIFICAND_SIZE_OF_FLOAT)
== value;
+     * }
+     *
+     * For negative values, this method behaves as if the value was positive.
+     * For NaN and infinite value, the returned value is undetermined.
+     *
+     * @param  value The value for which to get the significand.
+     * @return The significand of the given value.
+     */
+    public static int getSignificand(final float value) {
+        int bits = Float.floatToRawIntBits(value);
+        final int exponent = bits & (0xFF << SIGNIFICAND_SIZE_OF_FLOAT);
+        bits &= (1L << SIGNIFICAND_SIZE_OF_FLOAT) - 1;
+        if (exponent != 0) {
+            bits |= (1L << SIGNIFICAND_SIZE_OF_FLOAT);
+        } else {
+            bits <<= 1;
+        }
+        return bits;
     }
 }

Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/math/MathFunctions.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/math/MathFunctions.java?rev=1538039&r1=1538038&r2=1538039&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/math/MathFunctions.java
[UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/math/MathFunctions.java
[UTF-8] Fri Nov  1 19:16:36 2013
@@ -22,6 +22,7 @@ import org.apache.sis.util.ArraysExt;
 import org.apache.sis.util.Workaround;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.resources.Errors;
+import org.apache.sis.internal.util.Numerics;
 import org.apache.sis.internal.util.DoubleDouble;
 
 import static java.lang.Float.intBitsToFloat;
@@ -73,6 +74,25 @@ public final class MathFunctions extends
 
     /**
      * The logarithm of 2 in base 10, which is approximated by {@value}.
+     * This constant is useful for converting a power of 2 to a power of 10 as below:
+     *
+     * {@preformat java
+     *   double exp10 = exp2 * LOG10_2;
+     * }
+     *
+     * <blockquote><font size="-1">
+     * <b>Tip:</b> for <em>integer</em> values in the [-2620 …
2620] range, the following expression:
+     *
+     * {@preformat java
+     *   int exp10 = (int) Math.floor(exp2 * LOG10_2);
+     * }
+     *
+     * can be approximated using only integer arithmetic by:
+     *
+     * {@preformat java
+     *   int exp10 = (exp2 * 315653) >> 20;
+     * }
+     * </font></blockquote>
      *
      * @see Math#log10(double)
      * @see #getExponent(double)
@@ -363,7 +383,7 @@ public final class MathFunctions extends
          */
         final int exponent = Math.getExponent(value);
         if (exponent <= Double.MAX_EXPONENT) { // Exclude NaN and ±∞ cases.
-            return (int) -Math.floor(LOG10_2 * (exponent - SIGNIFICAND_SIZE));
+            return -Numerics.toExp10(exponent - SIGNIFICAND_SIZE);
         }
         return 0;
     }

Modified: sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/internal/util/DoubleDoubleTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/internal/util/DoubleDoubleTest.java?rev=1538039&r1=1538038&r2=1538039&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/internal/util/DoubleDoubleTest.java
[UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/internal/util/DoubleDoubleTest.java
[UTF-8] Fri Nov  1 19:16:36 2013
@@ -24,7 +24,6 @@ import org.apache.sis.util.ArraysExt;
 import org.apache.sis.test.TestCase;
 import org.apache.sis.test.TestUtilities;
 import org.apache.sis.test.DependsOnMethod;
-import org.apache.sis.test.DependsOn;
 import org.junit.Test;
 
 import static org.junit.Assert.*;
@@ -40,7 +39,6 @@ import static java.lang.StrictMath.*;
  * @version 0.4
  * @module
  */
-@DependsOn(org.apache.sis.math.MathFunctionsTest.class)
 public final strictfp class DoubleDoubleTest extends TestCase {
     /**
      * Number of time to repeat arithmetic tests.
@@ -293,9 +291,10 @@ public final strictfp class DoubleDouble
      */
     private static final String[] PREDEFINED_VALUES = {
          "0.000001",
+         "0.000004848136811095359935899141023579480",
          "0.00001",
          "0.0001",
-         "0.00027777777777777777777777777777777778",
+         "0.0002777777777777777777777777777777778",
          "0.001",
          "0.002777777777777777777777777777777778",
          "0.01",
@@ -375,6 +374,7 @@ public final strictfp class DoubleDouble
         assertEquals(6.123233995736766036E-17, DoubleDouble.errorForWellKnownValue(PI / 2),
STRICT);
         assertEquals(3.061616997868383018E-17, DoubleDouble.errorForWellKnownValue(PI / 4),
STRICT);
         assertEquals(9.184850993605148436E-17, DoubleDouble.errorForWellKnownValue(PI * (3./4)),
STRICT);
+        assertEquals(9.320078015422868E-23,    DoubleDouble.errorForWellKnownValue(PI / (180
* 60 * 60)), STRICT);
 
         assertTrue("toDegrees", DoubleDouble.errorForWellKnownValue(180 / PI)     != 0);
         assertTrue("toDegrees", DoubleDouble.errorForWellKnownValue(toDegrees(1)) != 0);
@@ -392,6 +392,7 @@ public final strictfp class DoubleDouble
             final DoubleDouble dd;
             switch (i) {
                 case 0:  dd = DoubleDouble.createDegreesToRadians(); break;
+                case 1:  dd = DoubleDouble.createSecondsToRadians(); break;
                 default: return; // Test done.
             }
             assertEquals(DoubleDouble.errorForWellKnownValue(dd.value), dd.error, STRICT);

Modified: sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/internal/util/NumericsTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/internal/util/NumericsTest.java?rev=1538039&r1=1538038&r2=1538039&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/internal/util/NumericsTest.java
[UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/internal/util/NumericsTest.java
[UTF-8] Fri Nov  1 19:16:36 2013
@@ -16,6 +16,9 @@
  */
 package org.apache.sis.internal.util;
 
+import java.util.Random;
+import org.apache.sis.math.MathFunctions;
+import org.apache.sis.test.TestUtilities;
 import org.apache.sis.test.TestCase;
 import org.junit.Test;
 
@@ -23,7 +26,7 @@ import static java.lang.Double.NaN;
 import static java.lang.Double.POSITIVE_INFINITY;
 import static java.lang.Double.NEGATIVE_INFINITY;
 import static org.apache.sis.internal.util.Numerics.*;
-import static org.apache.sis.test.Assert.*;
+import static org.junit.Assert.*;
 
 
 /**
@@ -36,7 +39,7 @@ import static org.apache.sis.test.Assert
  */
 public final strictfp class NumericsTest extends TestCase {
     /**
-     * Tests the {@link Utilities#epsilonEqual(double, double)} method.
+     * Tests the {@link Numerics#epsilonEqual(double, double)} method.
      */
     @Test
     public void testEpsilonEqual() {
@@ -51,4 +54,55 @@ public final strictfp class NumericsTest
         assertTrue (epsilonEqual(-100, -100 + COMPARISON_THRESHOLD * 50));
         assertFalse(epsilonEqual( 100,  100 + COMPARISON_THRESHOLD * 150));
     }
+
+    /**
+     * Tests the {@link Numerics#toExp10(int)} method over the full [-2620 … 2620] range
of values.
+     * This is the range documented as valid.
+     */
+    @Test
+    public void testToExp10() {
+        for (int i=-2620; i<=2620; i++) {
+            assertEquals(Math.floor(i * MathFunctions.LOG10_2), toExp10(i), 0);
+        }
+    }
+
+    /**
+     * Tests the {@link Numerics#getSignificand(double)} method.
+     */
+    @Test
+    public void testGetSignificand() {
+        assertEquals(0x00000000000000L, getSignificand(0d));
+        assertEquals(0x10000000000000L, getSignificand(1d));
+        assertEquals(0x1F400000000000L, getSignificand(1000d));
+        assertEquals(0x1FFFFFFFFFFFFFL, getSignificand(Double.MAX_VALUE));
+        assertEquals(0x10000000000000L, getSignificand(Double.MIN_NORMAL));
+        assertEquals(0x00000000000002L, getSignificand(Double.MIN_VALUE));
+        final Random random = TestUtilities.createRandomNumberGenerator();
+        for (int i=0; i<100; i++) {
+            final double value       = random.nextGaussian();
+            final double significand = getSignificand(value);
+            final double recomposed  = StrictMath.scalb(significand, StrictMath.getExponent(value)
- SIGNIFICAND_SIZE);
+            assertEquals(value, StrictMath.copySign(recomposed, value), 0);
+        }
+    }
+
+    /**
+     * Tests the {@link Numerics#getSignificand(float)} method.
+     */
+    @Test
+    public void testGetSignificandOfFloat() {
+        assertEquals(0x000000, getSignificand(0f));
+        assertEquals(0x800000, getSignificand(1f));
+        assertEquals(0xFA0000, getSignificand(1000f));
+        assertEquals(0xFFFFFF, getSignificand(Float.MAX_VALUE));
+        assertEquals(0x800000, getSignificand(Float.MIN_NORMAL));
+        assertEquals(0x000002, getSignificand(Float.MIN_VALUE));
+        final Random random = TestUtilities.createRandomNumberGenerator();
+        for (int i=0; i<100; i++) {
+            final float value       = (float) random.nextGaussian();
+            final float significand = getSignificand(value);
+            final float recomposed  = StrictMath.scalb(significand, StrictMath.getExponent(value)
- SIGNIFICAND_SIZE_OF_FLOAT);
+            assertEquals(value, StrictMath.copySign(recomposed, value), 0);
+        }
+    }
 }

Modified: sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/math/MathFunctionsTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/math/MathFunctionsTest.java?rev=1538039&r1=1538038&r2=1538039&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/math/MathFunctionsTest.java
[UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/math/MathFunctionsTest.java
[UTF-8] Fri Nov  1 19:16:36 2013
@@ -39,7 +39,10 @@ import org.apache.sis.internal.jdk8.JDK8
  * @version 0.4
  * @module
  */
-@DependsOn(org.apache.sis.util.ArraysExtTest.class)
+@DependsOn({
+    org.apache.sis.util.ArraysExtTest.class,
+    org.apache.sis.internal.util.NumericsTest.class
+})
 public final strictfp class MathFunctionsTest extends TestCase {
     /**
      * Small number for floating point comparisons.

Modified: sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java?rev=1538039&r1=1538038&r2=1538039&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java
[UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java
[UTF-8] Fri Nov  1 19:16:36 2013
@@ -36,6 +36,9 @@ import org.junit.BeforeClass;
     org.apache.sis.internal.test.XMLComparatorTest.class,
 
     // Most basic functions of SIS library.
+    org.apache.sis.internal.jdk8.JDK8Test.class,
+    org.apache.sis.internal.util.NumericsTest.class,
+    org.apache.sis.internal.util.DoubleDoubleTest.class,
     org.apache.sis.setup.OptionKeyTest.class,
     org.apache.sis.util.ArraysExtTest.class,
     org.apache.sis.util.CharactersTest.class,
@@ -57,9 +60,6 @@ import org.junit.BeforeClass;
     org.apache.sis.math.StatisticsTest.class,
     org.apache.sis.math.StatisticsFormatTest.class,
     org.apache.sis.internal.util.UtilitiesTest.class,
-    org.apache.sis.internal.util.NumericsTest.class,
-    org.apache.sis.internal.util.DoubleDoubleTest.class,
-    org.apache.sis.internal.jdk8.JDK8Test.class,
 
     // Collections.
     org.apache.sis.internal.util.CheckedArrayListTest.class,



Mime
View raw message