sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1534788 - in /sis/branches/JDK7/core: sis-referencing/src/main/java/org/apache/sis/io/wkt/ sis-utility/src/main/java/org/apache/sis/internal/jdk8/ sis-utility/src/main/java/org/apache/sis/internal/util/ sis-utility/src/main/java/org/apache...
Date Tue, 22 Oct 2013 21:07:24 GMT
Author: desruisseaux
Date: Tue Oct 22 21:07:24 2013
New Revision: 1534788

URL: http://svn.apache.org/r1534788
Log:
Math functions.

Modified:
    sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Formatter.java
    sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jdk8/JDK8.java
    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/main/java/org/apache/sis/measure/AngleFormat.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/math/MathFunctionsTest.java

Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Formatter.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Formatter.java?rev=1534788&r1=1534787&r2=1534788&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Formatter.java
[UTF-8] (original)
+++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Formatter.java
[UTF-8] Tue Oct 22 21:07:24 2013
@@ -702,7 +702,7 @@ public class Formatter {
          * a unit conversion). In the case of WGS84 semi-major axis in metres, we still have
a
          * maximum of 8 fraction digits, which is more than enough.
          */
-        numberFormat.setMaximumFractionDigits(MathFunctions.fractionDigitsForDelta(Math.ulp(number),
false) - 2);
+        numberFormat.setMaximumFractionDigits(MathFunctions.fractionDigitsForValue(number)
- 2);
         numberFormat.setMinimumFractionDigits(1); // Must be after setMaximumFractionDigits(…).
         numberFormat.format(number, buffer, dummy);
         resetColor();
@@ -819,6 +819,8 @@ public class Formatter {
 
     /**
      * Returns the WKT formatted by this object.
+     *
+     * @return The WKT formatted by this instance.
      */
     @Override
     public String toString() {

Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jdk8/JDK8.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jdk8/JDK8.java?rev=1534788&r1=1534787&r2=1534788&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jdk8/JDK8.java
[UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/jdk8/JDK8.java
[UTF-8] Tue Oct 22 21:07:24 2013
@@ -32,7 +32,7 @@ import org.apache.sis.util.CharSequences
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.3
- * @version 0.3
+ * @version 0.4
  * @module
  */
 public final class JDK8 {
@@ -50,6 +50,18 @@ public final class JDK8 {
     }
 
     /**
+     * Returns the floating-point value adjacent to {@code value} in the direction of negative
infinity.
+     *
+     * @param  value The value for which to get the adjacent value.
+     * @return The adjacent value in the direction of negative infinity.
+     *
+     * @since 0.4
+     */
+    public static double nextDown(final double value) {
+        return Math.nextAfter(value, Double.NEGATIVE_INFINITY);
+    }
+
+    /**
      * Parses a date from a string in ISO 8601 format. More specifically, this method expects
the
      * format defined by <cite>XML Schema Part 2: Datatypes for {@code xsd:dateTime}</cite>,
with
      * some additional flexibility (e.g. missing minutes or seconds fields are automatically
added).

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=1534788&r1=1534787&r2=1534788&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] Tue Oct 22 21:07:24 2013
@@ -85,10 +85,10 @@ public final class DoubleDouble extends 
 
     /**
      * When computing <var>a</var> - <var>b</var> as a double-double
(106 significand bits) value,
-     * if the amount of non-zero significand bits is equals or lower than that amount, consider
the
-     * result as zero.
+     * if the amount of non-zero significand bits is equals or lower than {@code ZERO_THRESHOLD+1},
+     * consider the result as zero.
      */
-    private static final int ZERO_THRESHOLD = 3;
+    private static final int ZERO_THRESHOLD = 2;
 
     /**
      * The split constant used as part of multiplication algorithms. The split algorithm
is as below
@@ -212,7 +212,30 @@ public final class DoubleDouble extends 
     public DoubleDouble() {
     }
 
-    /** Returns {@link #value}. */
+    /**
+     * Creates a new value initialized to the given value and error.
+     * It is caller's responsibility to ensure that the (value, error) pair is normalized.
+     *
+     * @param value The initial value.
+     * @param error The initial error.
+     */
+    public DoubleDouble(final double value, final double error) {
+        this.value = value;
+        this.error = error;
+        assert !(Math.abs(error) > Math.ulp(value)) : this; // Use ! for being tolerant
to NaN.
+    }
+
+    /**
+     * Returns a new {@code DoubleDouble} instance initialized to the conversion factor
+     * from angular degrees to radians.
+     *
+     * @return An instance initialize to the 0.01745329251994329576923690768488613 value.
+     */
+    public static DoubleDouble createDegreesToRadians() {
+        return new DoubleDouble(0.01745329251994329576923690768488613, 2.9486522708701687E-19);
+    }
+
+    /** @return {@link #value}. */
     @Override public double doubleValue() {return value;}
     @Override public float  floatValue()  {return (float) value;}
     @Override public long   longValue()   {return Math.round(value);}
@@ -436,7 +459,7 @@ public final class DoubleDouble extends 
              * The number of significand bits (mantissa) in the IEEE 'double' representation
is 52,
              * not counting the hidden bit. So estimate the accuracy of the double-double
number as
              * the accuracy of the 'double' value (which is 1 ULP) scaled as if we had 52
additional
-             * significand bits (we ignore some more bits if ZERO_THRESHOLD is greater than
1).
+             * significand bits (we ignore some more bits if ZERO_THRESHOLD is greater than
0).
              * If the error is not greater than that value, then assume that it is not significant.
              */
             if (Math.abs(error) <= Math.scalb(Math.ulp(otherValue), ZERO_THRESHOLD - Numerics.SIGNIFICAND_SIZE))
{

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=1534788&r1=1534787&r2=1534788&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] Tue Oct 22 21:07:24 2013
@@ -78,9 +78,9 @@ public final class Numerics extends Stat
 
     /**
      * Number of bits in the significand (mantissa) part of IEEE 754 {@code double} representation,
-     * including the hidden bit.
+     * <strong>not</strong> including the hidden bit.
      */
-    static final int SIGNIFICAND_SIZE = 53;
+    public static final int SIGNIFICAND_SIZE = 52;
 
     /**
      * A prime number used for hash code computation. Value 31 is often used because

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=1534788&r1=1534787&r2=1534788&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] Tue Oct 22 21:07:24 2013
@@ -22,13 +22,14 @@ 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.DoubleDouble;
 
 import static java.lang.Float.intBitsToFloat;
 import static java.lang.Float.floatToRawIntBits;
 import static java.lang.Double.longBitsToDouble;
 import static java.lang.Double.doubleToRawLongBits;
-import org.apache.sis.internal.util.DoubleDouble;
 import static org.apache.sis.internal.util.Numerics.SIGN_BIT_MASK;
+import static org.apache.sis.internal.util.Numerics.SIGNIFICAND_SIZE;
 
 
 /**
@@ -71,6 +72,26 @@ public final class MathFunctions extends
     public static final double SQRT_2 = 1.4142135623730951;
 
     /**
+     * The logarithm of 2 in base 10, which is {@value}.
+     *
+     * @see Math#log10(double)
+     * @see #fractionDigitsForValue(double)
+     *
+     * @since 0.4
+     */
+    public static final double LOG10_2 = 0.3010299956639812;
+
+    /**
+     * The greatest power of 10 such as {@code Math.pow(10, E10_FOR_ZERO) == 0}.
+     * This is the exponent in {@code parseDouble("1E-324")} &lt; {@link Double#MIN_VALUE},
+     * which is stored as zero because non-representable as a {@code double} value.
+     * The next power, {@code parseDouble("1E-323")}, is a non-zero {@code double} value.
+     *
+     * @see Double#MIN_VALUE
+     */
+    static final int E10_FOR_ZERO = -324;
+
+    /**
      * Table of some integer powers of 10. Used for faster computation in {@link #pow10(int)}.
      *
      * @see #pow10(int)
@@ -219,19 +240,29 @@ public final class MathFunctions extends
      *
      * <p>Special cases:</p>
      * <ul>
-     *   <li>If {@code accuracy} is 0, {@link Double#NaN NaN} or infinity,
-     *       then this method returns 0.</li>
+     *   <li>If {@code accuracy} is {@link Double#NaN NaN} or infinity, then this method
returns 0
+     *       since those values are not represented by decimal digits.</li>
+     *
+     *   <li>If {@code accuracy} is 0, then this method returns 324 since 10<sup>-324</sup>
is the
+     *       first power of 10 smaller than {@link Double#MIN_VALUE}.
+     *
+     *       {@note The above value can be understood in an other way: if the first 324 fraction
digits are zero,
+     *              then the IEEE <code>double</code> value is guaranteed to
be rounded to exactly 0 no matter
+     *              what the next fraction digits are.}</li>
+     *
      *   <li>If {@code accuracy} is greater than 1, then this method returns
      *       the number of "unnecessary" trailing zeros as a negative number.
-     *       For example {@code fractionDigitsForDelta(100, …)} returns -2.</li>
+     *
+     *       {@example <code>fractionDigitsForDelta(100, …)</code> returns
-2.}</li>
+     *
      *   <li>If the first non-zero digits of {@code accuracy} are equal or greater
than 95
      *       (e.g. 0.00099) and the {@code strict} argument is {@code true}, then this method
      *       increases the number of needed fraction digits in order to prevent the rounded
      *       number to be collapsed into the next integer value.
      *
      *       {@example
-     *       If {@code accuracy} is 0.95, then a return value of 1 is not sufficient since
the
-     *       rounded value of 0.95 with 1 fraction digit would be 1.0. Such value would be
a
+     *       If <code>accuracy</code> is 0.95, then a return value of 1 is not
sufficient since
+     *       the rounded value of 0.95 with 1 fraction digit would be 1.0. Such value would
be a
      *       violation of this method contract since the difference between 0 and that formatted
      *       value would be greater than the accuracy. Note that this is not an artificial
rule;
      *       this is related to the fact that 0.9999… is mathematically strictly equals
to 1.}</li>
@@ -274,20 +305,124 @@ public final class MathFunctions extends
         } else { // 'x' is out of range or NaN.
             final double y = Math.log10(accuracy);
             if (Double.isInfinite(y)) {
-                return 0;
+                return (accuracy == 0) ? -E10_FOR_ZERO : 0;
             }
             i = -((int) Math.floor(y));
             scale = pow10(i);
         }
-        while ((accuracy *= scale) >= 9.5) {
-            i++; // The 0.…95 special case.
-            accuracy -= Math.floor(accuracy);
-            scale = 10;
+        if (strict) {
+            while ((accuracy *= scale) >= 9.5) {
+                i++; // The 0.…95 special case.
+                accuracy -= Math.floor(accuracy);
+                scale = 10;
+            }
         }
         return i;
     }
 
     /**
+     * Returns the number of significant fraction digits when formatting the given number
in base 10.
+     * This method does <strong>not</strong> ignore trailing zeros.
+     * For example {@code fractionDigitsForValue(1.0)} returns 16,
+     * because the {@code double} format can store <i>almost</i> 16 decimal digits
after 1.
+     *
+     * {@note We said <i>almost</i> because the very last digit may be able to
store only a subset of the
+     *        [0 … 9] digits.}
+     *
+     * Invoking this method is equivalent to invoking <code>{@linkplain #fractionDigitsForDelta(double,
boolean)
+     * fractionDigitsForDelta}(Math.{@linkplain Math#ulp(double) ulp}(value), false)</code>,
except that it is
+     * potentially faster.
+     *
+     * <p>Special cases:</p>
+     * <ul>
+     *   <li>If {@code value} is {@link Double#NaN NaN} or infinity, then this method
returns 0
+     *       since those values are not represented by decimal digits.</li>
+     *
+     *   <li>If {@code value} is 0, then this method returns 324 since
+     *       {@code Math.ulp(0)} = {@value java.lang.Double#MIN_VALUE}.</li>
+     * </ul>
+     *
+     * {@example This method is useful with <code>NumberFormat</code> for formatting
all significant digits
+     *           of a <code>double</code> value, padding with trailing zeros
if necessary, but no more than
+     *           necessary.}
+     *
+     * @param  value The value for which to get the number of significant digits.
+     * @return The number of significant digits (may be negative), or 0 if {@code value}
is NaN or infinity.
+     *
+     * @see java.text.NumberFormat#setMinimumFractionDigits(int)
+     *
+     * @since 0.4
+     */
+    public static int fractionDigitsForValue(final double value) {
+        /*
+         * We really need Math.getExponent(value) here rather than MathFunctions.getExponent(value).
+         * What we actually want is MathFunctions.getExponent(Math.ulp(value)), but we get
the same
+         * result more efficiently if we replace the call to Math.ulp(double) by a SIGNIFICAND_SIZE
+         * subtraction in the exponent, provided that the exponent has NOT been corrected
for sub-
+         * normal numbers (in order to reproduce the Math.ulp behavior).
+         */
+        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 0;
+    }
+
+    /**
+     * Returns the unbiased exponent used in the representation of a {@code double}, with
correction for
+     * sub-normal numbers. This method is related to {@link Math#getExponent(double)} in
the following ways:
+     *
+     * <ul>
+     *   <li>For NaN and all values equal or greater than {@link Double#MIN_NORMAL}
in magnitude (including
+     *       infinities), this method returns results that are identical to {@code Math.getExponent(double)}.</li>
+     *   <li>For values smaller than {@link Double#MIN_NORMAL} in magnitude (including
zero), the correction
+     *       for sub-normal numbers results in return values smaller than what {@code Math.getExponent(double)}
+     *       would return.</li>
+     * </ul>
+     *
+     * Special cases:
+     * <ul>
+     *   <li>If the argument is NaN or infinite, then the result is {@link Double#MAX_EXPONENT}
+ 1.</li>
+     *   <li>If the argument is {@link Double#MAX_VALUE},  then the result is {@value
java.lang.Double#MAX_EXPONENT}.</li>
+     *   <li>If the argument is {@link Double#MIN_NORMAL}, then the result is {@value
java.lang.Double#MIN_EXPONENT}.</li>
+     *   <li>If the argument is {@link Double#MIN_VALUE},  then the result is -1074.</li>
+     *   <li>If the argument is zero, then the result is -1075.</li>
+     * </ul>
+     *
+     * {@section Identities}
+     * For any <var>p</var> values in the [-1075 … 1024] range and <var>value</var>
= 2<sup>p</sup>:
+     * <ul>
+     *   <li><code>getExponent(Math.scalb(1.0, p)) == p</code></li>
+     *   <li><code>Math.scalb(1.0, getExponent(value)) == value</code></li>
+     *   <li><code>Math.floor({@linkplain #LOG10_2} * getExponent(value)) ==
Math.floor(Math.log10(value))</code></li>
+     * </ul>
+     *
+     * @param  value The value for which to get the exponent.
+     * @return The unbiased exponent, corrected for sub-normal numbers if needed.
+     *         Values will be in the [-1075 … 1024] range, inclusive.
+     *
+     * @see Math#getExponent(double)
+     * @see Math#scalb(double, int)
+     *
+     * @since 0.4
+     */
+    public static int getExponent(final double value) {
+        final long bits = doubleToRawLongBits(value);
+        int exponent = (int) ((bits >>> SIGNIFICAND_SIZE) & 0x7FFL);
+        if (exponent == 0) {
+            /*
+             * Number is sub-normal: there is no implicit 1 bit before the significand.
+             * We need to search for the position of the first real 1 bit, and fix the
+             * exponent accordingly.  Note that numberOfLeadingZeros(…) is relative to
+             * 64 bits while the significand size is only 52 bits. The last term below
+             * is for fixing this difference.
+             */
+            exponent -= Long.numberOfLeadingZeros(bits & ((1L << SIGNIFICAND_SIZE)
- 1)) - (Long.SIZE - SIGNIFICAND_SIZE);
+        }
+        return exponent - Double.MAX_EXPONENT;
+    }
+
+    /**
      * Computes 10 raised to the power of <var>x</var>. This method delegates
to
      * <code>{@linkplain #pow10(int) pow10}((int) x)</code> if <var>x</var>
is an
      * integer, or to <code>{@linkplain Math#pow(double, double) Math.pow}(10, x)</code>
@@ -529,9 +664,9 @@ public final class MathFunctions extends
      * @return {@code +1} if <var>x</var> is positive, {@code -1} if negative,
or 0 otherwise.
      */
     public static short sgn(short x) {
-        if (x > 0) return (short) +1;
-        if (x < 0) return (short) -1;
-        else       return (short)  0;
+        if (x > 0) return +1;
+        if (x < 0) return -1;
+        else       return  0;
     }
 
     /**
@@ -544,9 +679,9 @@ public final class MathFunctions extends
      * @return {@code +1} if <var>x</var> is positive, {@code -1} if negative,
or 0 otherwise.
      */
     public static byte sgn(byte x) {
-        if (x > 0) return (byte) +1;
-        if (x < 0) return (byte) -1;
-        else       return (byte)  0;
+        if (x > 0) return +1;
+        if (x < 0) return -1;
+        else       return  0;
     }
 
     /**

Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/measure/AngleFormat.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/measure/AngleFormat.java?rev=1534788&r1=1534787&r2=1534788&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/measure/AngleFormat.java
[UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/measure/AngleFormat.java
[UTF-8] Tue Oct 22 21:07:24 2013
@@ -39,7 +39,7 @@ import static java.lang.Double.isInfinit
 import static org.apache.sis.math.MathFunctions.pow10;
 import static org.apache.sis.math.MathFunctions.truncate;
 import static org.apache.sis.math.MathFunctions.isNegative;
-import static org.apache.sis.math.MathFunctions.fractionDigitsForDelta;
+import static org.apache.sis.math.MathFunctions.fractionDigitsForValue;
 
 // Related to JDK7
 import java.util.Objects;
@@ -956,7 +956,7 @@ public class AngleFormat extends Format 
         if (maximumFractionDigits != minimumFractionDigits) {
             if      (secondsFieldWidth != 0) angle *= 3600;
             else if (minutesFieldWidth != 0) angle *=   60;
-            final int n = fractionDigitsForDelta(Math.ulp(angle), false) - 1;
+            final int n = fractionDigitsForValue(angle) - 1;
             if (n < maximumFractionDigits) {
                 maximumFractionDigits = Math.max(minimumFractionDigits, n);
             }
@@ -1039,12 +1039,16 @@ public class AngleFormat extends Format 
                 if (suffix != null) {
                     toAppendTo.append(suffix);
                 }
-                it.addFieldLimit(Field.forCode(field), hasMore
-                        ? (Number) Integer.valueOf((int) Math.round(value))
-                        : (Number) Float.valueOf((float) value), startPosition);
-                // The 'valueOf(…)' was for information purpose only. We use Float instead
of Double
-                // because we don't want to give a false impression of accuracy (when formatting
the
-                // seconds field, at least the 10 last bits of the double value are non-significant).
+                final Number userObject;
+                if (hasMore) {
+                    userObject = (int) Math.round(value);
+                } else {
+                    // Use Float instead of Double because we don't want to give a false
impression of accuracy
+                    // (when formatting the seconds field, at least the 10 last bits of the
'double' value are
+                    // non-significant).
+                    userObject = (float) value;
+                }
+                it.addFieldLimit(Field.forCode(field), userObject, startPosition);
             } else {
                 toAppendTo = numberFormat.format(value, toAppendTo, dummyFieldPosition());
                 if (suffix != null) {
@@ -1746,6 +1750,8 @@ BigBoss:    switch (skipSuffix(source, p
 
     /**
      * Returns a clone of this {@code AngleFormat}.
+     *
+     * @return A clone of this format.
      */
     @Override
     public AngleFormat clone() {

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=1534788&r1=1534787&r2=1534788&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] Tue Oct 22 21:07:24 2013
@@ -252,6 +252,42 @@ public final strictfp class DoubleDouble
     }
 
     /**
+     * Tests {@link DoubleDouble#sqrt()} first with the square root of 2, then with random
values.
+     * In the {@code sqrt(2)} case:
+     *
+     * <ul>
+     *   <li>The error using {@code double} arithmetic is approximatively 1E-16.</li>
+     *   <li>The error using double-double arithmetic is expected to be slightly less
that 1E-32.</li>
+     * </ul>
+     */
+    @Test
+    @DependsOnMethod({"testMultiply", "testDivide"})
+    public void testSqrt() {
+        final BigDecimal SQRT2 = new BigDecimal("1.414213562373095048801688724209698");
+        final DoubleDouble dd = new DoubleDouble(2, 0);
+        dd.sqrt();
+        assertNormalizedAndEquals(sqrt(2), dd);
+        assertEquals(0, SQRT2.subtract(toBigDecimal(dd)).doubleValue(), 1E-32);
+        /*
+         * If we have been able to compute √2, now test with random values.
+         * Since the range of values is approximatively [-1000 … 1000], use
+         * a tolerance value 1000 time the one that we used for √2.
+         */
+        for (int i=0; i<NUMBER_OF_REPETITIONS; i++) {
+            nextRandom(dd);
+            if (dd.value < 0) {
+                dd.negate();
+            }
+            final double value = dd.value;
+            final double error = dd.error;
+            dd.multiply(dd);
+            dd.sqrt();
+            dd.subtract(value, error);
+            assertEquals(0, dd.doubleValue(), 1E-29);
+        }
+    }
+
+    /**
      * List of all {@link DoubleDouble#VALUES} as string decimal representation.
      */
     private static final String[] PREDEFINED_VALUES = {
@@ -346,39 +382,18 @@ public final strictfp class DoubleDouble
     }
 
     /**
-     * Tests {@link DoubleDouble#sqrt()} first with the square root of 2, then with random
values.
-     * In the {@code sqrt(2)} case:
-     *
-     * <ul>
-     *   <li>The error using {@code double} arithmetic is approximatively 1E-16.</li>
-     *   <li>The error using double-double arithmetic is expected to be slightly less
that 1E-32.</li>
-     * </ul>
+     * Tests the {@code DoubleDouble.createFoo()} methods.
      */
     @Test
-    @DependsOnMethod({"testMultiply", "testDivide"})
-    public void testSqrt() {
-        final BigDecimal SQRT2 = new BigDecimal("1.414213562373095048801688724209698");
-        final DoubleDouble dd = new DoubleDouble();
-        dd.value = 2;
-        dd.sqrt();
-        assertNormalizedAndEquals(sqrt(2), dd);
-        assertEquals(0, SQRT2.subtract(toBigDecimal(dd)).doubleValue(), 1E-32);
-        /*
-         * If we have been able to compute √2, now test with random values.
-         * Since the range of values is approximatively [-1000 … 1000], use
-         * a tolerance value 1000 time the one that we used for √2.
-         */
-        for (int i=0; i<NUMBER_OF_REPETITIONS; i++) {
-            nextRandom(dd);
-            if (dd.value < 0) {
-                dd.negate();
+    @DependsOnMethod("testErrorForWellKnownValue")
+    public void testCreate() {
+        for (int i=0; ; i++) {
+            final DoubleDouble dd;
+            switch (i) {
+                case 0:  dd = DoubleDouble.createDegreesToRadians(); break;
+                default: return; // Test done.
             }
-            final double value = dd.value;
-            final double error = dd.error;
-            dd.multiply(dd);
-            dd.sqrt();
-            dd.subtract(value, error);
-            assertEquals(0, dd.doubleValue(), 1E-29);
+            assertEquals(DoubleDouble.errorForWellKnownValue(dd.value), dd.error, STRICT);
         }
     }
 }

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=1534788&r1=1534787&r2=1534788&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] Tue Oct 22 21:07:24 2013
@@ -22,8 +22,13 @@ import org.apache.sis.test.DependsOn;
 import org.apache.sis.test.DependsOnMethod;
 
 import static org.junit.Assert.*;
+import static java.lang.Double.*;
 import static org.apache.sis.math.MathFunctions.*;
 import static org.apache.sis.util.ArraysExt.isSorted;
+import static org.apache.sis.internal.util.Numerics.SIGNIFICAND_SIZE;
+
+// Related to JDK8
+import org.apache.sis.internal.jdk8.JDK8;
 
 
 /**
@@ -31,7 +36,7 @@ import static org.apache.sis.util.Arrays
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @since   0.3 (derived from geotk-2.5)
- * @version 0.3
+ * @version 0.4
  * @module
  */
 @DependsOn(org.apache.sis.util.ArraysExtTest.class)
@@ -42,6 +47,13 @@ public final strictfp class MathFunction
     private static final double EPS = 1E-12;
 
     /**
+     * The maximal exponent value such as {@code parseDouble("1E+304")} still a finite number.
+     *
+     * @see Double#MAX_VALUE
+     */
+    private static final int MAX_E10 = 308;
+
+    /**
      * Highest prime number representable as a signed {@code short}.
      */
     private static final int HIGHEST_SHORT_PRIME = 32749;
@@ -52,6 +64,19 @@ public final strictfp class MathFunction
     private static final int LOWEST_USHORT_PRIME = 32771;
 
     /**
+     * Verifies the values of {@link MathFunctions#SQRT_2} and {@link MathFunctions#LOG10_2}.
+     */
+    @Test
+    public void testConstants() {
+        assertEquals(StrictMath.sqrt (2), MathFunctions.SQRT_2,  0);
+        assertEquals(StrictMath.log10(2), MathFunctions.LOG10_2, 0);
+        assertEquals(0,  parseDouble("1E" +  E10_FOR_ZERO), 0);
+        assertTrue  (0 < parseDouble("1E" + (E10_FOR_ZERO + 1)));
+        assertTrue  (POSITIVE_INFINITY > parseDouble("1E" +  MAX_E10));
+        assertEquals(POSITIVE_INFINITY,  parseDouble("1E" + (MAX_E10 + 1)), 0);
+    }
+
+    /**
      * Tests {@link MathFunctions#truncate(double)}.
      */
     @Test
@@ -60,18 +85,10 @@ public final strictfp class MathFunction
         assertEquals(-4.0, truncate(-4.9), 0);
         assertEquals(+0.0, truncate(+0.1), 0);
         assertEquals(-0.0, truncate(-0.1), 0);
-        assertEquals("Positive zero",
-                Double.doubleToLongBits(+0.0),
-                Double.doubleToLongBits(truncate(+0.5)));
-        assertEquals("Negative zero",
-                Double.doubleToLongBits(-0.0),
-                Double.doubleToLongBits(truncate(-0.5)));
-        assertEquals("Positive zero",
-                Double.doubleToLongBits(+0.0),
-                Double.doubleToLongBits(truncate(+0.0)));
-        assertEquals("Negative zero",
-                Double.doubleToLongBits(-0.0),
-                Double.doubleToLongBits(truncate(-0.0)));
+        assertEquals("Positive zero", doubleToLongBits(+0.0), doubleToLongBits(truncate(+0.5)));
+        assertEquals("Negative zero", doubleToLongBits(-0.0), doubleToLongBits(truncate(-0.5)));
+        assertEquals("Positive zero", doubleToLongBits(+0.0), doubleToLongBits(truncate(+0.0)));
+        assertEquals("Negative zero", doubleToLongBits(-0.0), doubleToLongBits(truncate(-0.0)));
     }
 
     /**
@@ -126,11 +143,91 @@ public final strictfp class MathFunction
         assertEquals(-22,  fractionDigitsForDelta(9.6E+23,  true)); // Special case
         assertEquals(-300, fractionDigitsForDelta(1.1E+300, true));
 
-        // Special cases.
-        assertEquals(0,  fractionDigitsForDelta(0, true));
-        assertEquals(0,  fractionDigitsForDelta(Double.NaN, true));
-        assertEquals(0,  fractionDigitsForDelta(Double.POSITIVE_INFINITY, true));
-        assertEquals(0,  fractionDigitsForDelta(Double.NEGATIVE_INFINITY, true));
+        // Other cases.
+        assertEquals(-E10_FOR_ZERO, fractionDigitsForDelta(0,                 false));
+        assertEquals(-E10_FOR_ZERO, fractionDigitsForDelta(MIN_VALUE,         false));
+        assertEquals(           16, fractionDigitsForDelta(Math.ulp(1.0),     false));
+        assertEquals(           15, fractionDigitsForDelta(Math.ulp(45.0),    false));
+        assertEquals(            0, fractionDigitsForDelta(NaN,               false));
+        assertEquals(            0, fractionDigitsForDelta(POSITIVE_INFINITY, false));
+        assertEquals(            0, fractionDigitsForDelta(NEGATIVE_INFINITY, false));
+    }
+
+    /**
+     * Tests {@link MathFunctions#fractionDigitsForValue(double)}.
+     */
+    @Test
+    public void testFractionDigitsForValue() {
+        assertEquals(-E10_FOR_ZERO, fractionDigitsForValue(0));
+        assertEquals(-E10_FOR_ZERO, fractionDigitsForValue(MIN_VALUE));
+        assertEquals(           16, fractionDigitsForValue(1));
+        assertEquals(           15, fractionDigitsForValue(45));
+        assertEquals(            0, fractionDigitsForValue(NaN));
+        assertEquals(            0, fractionDigitsForValue(POSITIVE_INFINITY));
+        assertEquals(            0, fractionDigitsForValue(NEGATIVE_INFINITY));
+        for (int i=E10_FOR_ZERO; i<=MAX_E10; i++) {
+            final double value = pow10(i);
+            final double accuracy = pow10(-fractionDigitsForValue(value));
+            assertEquals("Shall not be greater than ULP", 0, accuracy, StrictMath.ulp(value));
+        }
+        for (int i=MIN_EXPONENT; i<=MAX_EXPONENT; i++) {
+            final double value = StrictMath.scalb(1, i);
+            final double accuracy = pow10(-fractionDigitsForValue(value));
+            assertEquals("Shall not be greater than ULP", 0, accuracy, StrictMath.ulp(value));
+        }
+    }
+
+    /**
+     * Tests the {@link MathFunctions#getExponent(double)} method.
+     * This method performs two tests:
+     *
+     * <ul>
+     *   <li>First, tests with a few normal (non-subnormal) numbers.
+     *       The result shall be identical to {@link StrictMath#getExponent(double)}.</li>
+     *   <li>Then, test with a few sub-normal numbers.</li>
+     * </ul>
+     */
+    @Test
+    public void testGetExponent() {
+        final double[] normalValues = {
+            1E+300, 1E+200, 1E+100, 1E+10, 50, 20, 1, 1E-10, 1E-100, 1E-200, 1E-300,
+            POSITIVE_INFINITY,
+            NEGATIVE_INFINITY,
+            NaN,
+            MAX_VALUE,
+            MIN_NORMAL
+        };
+        for (final double value : normalValues) {
+            assertEquals(StrictMath.getExponent(value), getExponent(value));
+        }
+        /*
+         * Tests sub-normal values. We expect:
+         *
+         *   getExponent(MIN_NORMAL    )  ==  MIN_EXPONENT
+         *   getExponent(MIN_NORMAL / 2)  ==  MIN_EXPONENT - 1
+         *   getExponent(MIN_NORMAL / 4)  ==  MIN_EXPONENT - 2
+         *   getExponent(MIN_NORMAL / 8)  ==  MIN_EXPONENT - 3
+         *   etc.
+         */
+        for (int i=0; i<=SIGNIFICAND_SIZE; i++) {
+            assertEquals(MIN_EXPONENT - i, getExponent(MIN_NORMAL / (1L << i)));
+        }
+        assertEquals(MIN_EXPONENT - 1,                    getExponent(JDK8.nextDown(MIN_NORMAL)));
+        assertEquals(MIN_EXPONENT - SIGNIFICAND_SIZE,     getExponent(MIN_VALUE));
+        assertEquals(MIN_EXPONENT - SIGNIFICAND_SIZE - 1, getExponent(0));
+        /*
+         * Tests consistency with scalb, as documented in MathFunctions.getExponent(double)
javadoc.
+         */
+        for (int i = MIN_EXPONENT - SIGNIFICAND_SIZE - 1; i <= MAX_EXPONENT + 1; i++)
{
+            assertEquals(i, getExponent(StrictMath.scalb(1.0, i)));
+        }
+        /*
+         * Tests consistency with log10, as documented in MathFunctions.getExponent(double)
javadoc.
+         */
+        for (int i = MIN_EXPONENT - SIGNIFICAND_SIZE; i <= MAX_EXPONENT; i++) {
+            assertEquals(StrictMath.floor(StrictMath.log10(StrictMath.scalb(1.0, i))),
+                         StrictMath.floor(LOG10_2 * i /* i = getExponent(value) */), 0);
+        }
     }
 
     /**
@@ -140,8 +237,8 @@ public final strictfp class MathFunction
      */
     @Test
     public void testPow10() {
-        for (int i=-304; i<=304; i++) { // Range of allowed exponents in base 10.
-            assertEquals(Double.parseDouble("1E"+i), pow10((double) i), 0);
+        for (int i=E10_FOR_ZERO; i<=MAX_E10; i++) { // Range of allowed exponents in base
10.
+            assertEquals(parseDouble("1E"+i), pow10((double) i), 0);
         }
     }
 
@@ -154,9 +251,9 @@ public final strictfp class MathFunction
             final double x = 0.1 * i;
             final double y = atanh(x);
             switch (i) {
-                case -10: assertEquals(Double.NEGATIVE_INFINITY, y, EPS); break;
-                default:  assertEquals(x, StrictMath.tanh(y),       EPS); break;
-                case +10: assertEquals(Double.POSITIVE_INFINITY, y, EPS); break;
+                case -10: assertEquals(NEGATIVE_INFINITY, y,  EPS); break;
+                default:  assertEquals(x, StrictMath.tanh(y), EPS); break;
+                case +10: assertEquals(POSITIVE_INFINITY, y,  EPS); break;
             }
         }
     }



Mime
View raw message