Author: desruisseaux
Date: Thu Sep 26 20:02:11 2013
New Revision: 1526663

URL: http://svn.apache.org/r1526663
Log:
Added double-double arithmetic support as an internal class.
For now we plan to use it only for matrix multiplications and inversions, where
(in the particular case of SIS) accuracy is more critical than performance.

Added:
    
sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/util/DoubleDouble.java
   (with props)
    
sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/internal/util/DoubleDoubleTest.java
   (with props)
Modified:
    
sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java

Added: 
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=1526663&view=auto
==============================================================================
--- 
sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/util/DoubleDouble.java
 (added)
+++ 
sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/util/DoubleDouble.java
 [UTF-8] Thu Sep 26 20:02:11 2013
@@ -0,0 +1,411 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.internal.util;
+
+import java.util.Arrays;
+import org.apache.sis.math.MathFunctions;
+// No BigDecimal dependency - see class javadoc
+
+
+/**
+ * Basic arithmetic methods for extended precision numbers using the 
<cite>double-double</cite> algorithm.
+ * This class implements some of the methods published in the following paper:
+ *
+ * <ul>
+ *   <li>Yozo Hida, Xiaoye S. Li, David H. Bailey.
+ *       <a 
href="http://web.mit.edu/tabbott/Public/quaddouble-debian/qd-2.3.4-old/docs/qd.pdf";>Library
+ *       for Double-Double and Quad-Double arithmetic</a>, 2007.</li>
+ *   <li>Jonathan R. Shewchuk. Adaptive precision floating-point arithmetic 
and fast robust geometric predicates.
+ *       Discrete & Computational Geometry, 18(3):305–363, 1997.</li>
+ * </ul>
+ *
+ * {@code DoubleDouble} is used as an alternative to {@link 
java.math.BigDecimal} when we do not need arbitrary
+ * precision, we do not want to convert from base 2 to base 10, we need 
support for NaN and infinities, we want
+ * more compact storage and better performance. {@code DoubleDouble} can be 
converted to {@code BigDecimal} as
+ * below:
+ *
+ * {@preformat java
+ *     BigDecimal decimal = new BigDecimal(dd.value).add(new 
BigDecimal(dd.error));
+ * }
+ *
+ * We do not provide convenience method for the above in order to avoid 
dependency to {@code BigDecimal}.
+ *
+ * {@section Impact of availability of FMA instructions}
+ * If <cite>fused multiply-add</cite> (FMA) instruction are available in a 
future Java version
+ * (see <a href="https://issues.apache.org/jira/browse/SIS-136";>SIS-136</a> on 
Apache SIS JIRA),
+ * then the following methods should be revisited:
+ *
+ * <ul>
+ *   <li>{@link #setToProduct(double, double)} - revisit with [Hida & al.] 
algorithm 7.</li>
+ * </ul>
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.4
+ * @version 0.4
+ * @module
+ *
+ * @see <a 
href="http://en.wikipedia.org/wiki/Double-double_%28arithmetic%29#Double-double_arithmetic";>Double-double
 arithmetic</a>
+ */
+public final class DoubleDouble extends Number {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -7602414219228638550L;
+
+    /**
+     * The split constant used as part of multiplication algorithms. The split 
algorithm is as below
+     * (we have to inline it in multiplication methods because Java can not 
return multi-values):
+     *
+     * {@preformat java
+     *     private void split(double a) {
+     *         double t   = SPLIT * a;
+     *         double ahi = t - (t - a);
+     *         double alo = a - ahi;
+     *     }
+     * }
+     *
+     * <p>Source: [Hida & al.] page 4 algorithm 5, itself reproduced from 
[Shewchuk] page 325.</p>
+     */
+    private static final double SPLIT = (1 << 27) + 1;
+
+    /**
+     * Maximal value that can be handled by {@link #multiply(double, double)}.
+     * If a multiplication is using a value greater than {@code MAX_VALUE},
+     * then the result will be infinity or NaN.
+     */
+    public static final double MAX_VALUE = Double.MAX_VALUE / SPLIT;
+
+    /**
+     * Pre-defined constants frequently used in SIS. SIS often creates 
matrices for unit conversions,
+     * and most conversion factors are defined precisely in base 10. For 
example the conversion from
+     * feet to metre is defined by a factor of exactly 0.3048, which can not 
be represented precisely
+     * as a {@code double}. Consequently if a value of 0.3048 is given, we can 
assume that the intend
+     * was to provide the "feet to metres" conversion factor and complete the 
double-double instance
+     * accordingly.
+     *
+     * <p>Elements in this array shall be sorted in strictly increasing order.
+     * For any value at index <var>i</var>, the associated error is {@code 
ERRORS[i]}.
+     */
+    private static final double[] VALUES = {
+        0.000001,
+        0.00001,
+        0.0001,
+        0.001,
+        0.01,
+        0.1,
+        0.3048,     // Feet to metres
+        0.9         // Degrees to gradians
+    };
+
+    /**
+     * The errors associated to the values in the {@link #VALUES} array.
+     *
+     * <p>Tips:</p>
+     * <ul>
+     *   <li>To compute a new value in this array, just put zero and execute
+     *       {@code DoubleDoubleTest.testErrorForWellKnownValue()}.
+     *       The error message will give the expected value.</li>
+     *   <li>If a computed value is zero, then there is no point to create an 
entry
+     *       in the {@code (VALUES, ERRORS)} arrays for that value.</li>
+     * </ul>
+     */
+    private static final double[] ERRORS = {
+        /* 0.000001  */  4.525188817411374E-23,
+        /* 0.00001   */ -8.180305391403131E-22,
+        /* 0.0001    */ -4.79217360238593E-21,
+        /* 0.001     */ -2.0816681711721686E-20,
+        /* 0.01      */ -2.0816681711721684E-19,
+        /* 0.1       */ -5.551115123125783E-18,
+        /* 0.3048    */ -1.5365486660812166E-17,
+        /* 0.9       */ -2.2204460492503132E-17
+    };
+
+    /**
+     * The main value, minus the {@link #error}.
+     */
+    public double value;
+
+    /**
+     * The error that shall be added to the main {@link #value} in order to 
get the
+     * "<cite>real</cite>" (actually "<cite>the most accurate that we 
can</cite>") value.
+     */
+    public double error;
+
+    /**
+     * Creates a new value initialized to zero.
+     */
+    public DoubleDouble() {
+    }
+
+    /** Returns {@link #value}. */
+    @Override public double doubleValue() {return value;}
+    @Override public float  floatValue()  {return (float) value;}
+    @Override public long   longValue()   {return Math.round(value);}
+    @Override public int    intValue()    {return (int) longValue();}
+
+    /**
+     * If the given value is one of the well known constants, returns the 
error for that value.
+     * Otherwise returns 0.
+     *
+     * @param  value The value for which to get this error.
+     * @return The error for the given value, or 0 if unknown. In the later 
case,
+     *         the given value is assumed to be the most accurate available 
representation.
+     */
+    public static double errorForWellKnownValue(final double value) {
+        final int i = Arrays.binarySearch(VALUES, Math.abs(value));
+        return (i >= 0) ? MathFunctions.xorSign(ERRORS[i], value) : 0;
+    }
+
+    /**
+     * Equivalent to a call to {@code setToQuickSum(value, error)} inlined.
+     * This is invoked after addition or multiplication operations.
+     */
+    final void normalize() {
+        error += (value - (value += error));
+    }
+
+    /**
+     * Sets this {@code DoubleDouble} to the sum of the given numbers,
+     * to be used only when {@code abs(a) >= abs(b)}.
+     *
+     * <p>Source: [Hida & al.] page 4 algorithm 3, itself reproduced from 
[Shewchuk] page 312.</p>
+     *
+     * @param a The first number to add.
+     * @param b The second number to add, which must be smaller than {@code a}.
+     */
+    public void setToQuickSum(final double a, final double b) {
+        value = a + b;
+        error = b - (value - a);
+    }
+
+    /**
+     * Sets this {@code DoubleDouble} to the sum of the given numbers.
+     *
+     * <p>Source: [Hida & al.] page 4 algorithm 4, itself reproduced from 
[Shewchuk] page 314.</p>
+     *
+     * @param a The first number to add.
+     * @param b The second number to add.
+     */
+    public void setToSum(final double a, final double b) {
+        value = a + b;
+        final double v = value - a;
+        error = (a - (value - v)) + (b - v);
+    }
+
+    /**
+     * Sets this {@code DoubleDouble} to the product of the given numbers.
+     * The given numbers shall not be greater than {@value #MAX_VALUE} in 
magnitude.
+     *
+     * <p>Source: [Hida & al.] page 4 algorithm 6, itself reproduced from 
[Shewchuk] page 326.</p>
+     *
+     * @param a The first number to multiply.
+     * @param b The second number to multiply.
+     */
+    public void setToProduct(final double a, final double b) {
+        value = a * b;
+        double t = SPLIT * a;
+        final double ahi = t - (t - a);
+        final double alo = a - ahi;
+        t = SPLIT * b;
+        final double bhi = t - (t - b);
+        final double blo = b - bhi;
+        error = ((ahi*bhi - value) + ahi*blo + alo*bhi) + alo*blo;
+    }
+
+    /**
+     * Adds an other double-double value to this {@code DoubleDouble}.
+     * This is a convenience method for:
+     *
+     * {@preformat java
+     *    add(other.value, other.error);
+     * }
+     *
+     * @param other The other value to add to this value.
+     */
+    public void add(final DoubleDouble other) {
+        add(other.value, other.error);
+    }
+
+    /**
+     * Adds an other double-double value to this {@code DoubleDouble}.
+     * The result is stored in this instance.
+     *
+     * {@section Implementation}
+     * If <var>a</var> and <var>b</var> are {@code DoubleDouble} instances, 
then:
+     *
+     *   <blockquote>(a + b)</blockquote>
+     *
+     * can be computed as:
+     *
+     *   <blockquote>(a.value + a.error) + (b.value + b.error)<br>
+     *             = (a.value + b.value) + (a.error + b.error)</blockquote>
+     *
+     * keeping in mind that the result of (a.value + b.value) has itself an 
error
+     * which needs to be added to (a.error + b.error). In Java code:
+     *
+     * {@preformat java
+     *   final double thisError = this.error;
+     *   setToSum(value, otherValue);
+     *   error += thisError;
+     *   error += otherError;
+     *   setToQuickSum(value, error);
+     * }
+     *
+     * @param otherValue The other value to add to this {@code DoubleDouble}.
+     * @param otherError The error of the other value to add to this {@code 
DoubleDouble}.
+     */
+    public void add(final double otherValue, final double otherError) {
+        // Inline expansion of the code in above javadoc.
+        double v = value;
+        value += otherValue;
+        error += v - (value + (v -= value)) + (otherValue + v);
+        error += otherError;
+        normalize();
+    }
+
+    /**
+     * Multiplies this {@code DoubleDouble} by an other double-double value.
+     * This is a convenience method for:
+     *
+     * {@preformat java
+     *    multiply(other.value, other.error);
+     * }
+     *
+     * @param other The other value to add to this value.
+     */
+    public void multiply(final DoubleDouble other) {
+        multiply(other.value, other.error);
+    }
+
+    /**
+     * Multiplies this {@code DoubleDouble} by an other double-double value.
+     * The result is stored in this instance.
+     *
+     * {@section Implementation}
+     * If <var>a</var> and <var>b</var> are {@code DoubleDouble} instances, 
then:
+     *
+     *   <blockquote>(a * b)</blockquote>
+     *
+     * can be computed as:
+     *
+     *   <blockquote>(a.value + a.error) * (b.value + b.error)<br>
+     *             = (a.value * b.value) + (a.error * b.value) + (a.value * 
b.error) + (a.error * b.error)<br>
+     *             ≅ (a.value * b.value) + (a.error * b.value) + (a.value * 
b.error)</blockquote>
+     *
+     * The first term is the main product. All other terms are added to the 
error, keeping in mind that the main
+     * product has itself an error. The last term (the product of errors) is 
ignored because presumed very small.
+     * In Java code:
+     *
+     * {@preformat java
+     *   final double thisValue = this.value;
+     *   final double thisError = this.error;
+     *   setToProduct(thisValue, otherValue);
+     *   error += otherError * thisValue;
+     *   error += otherValue * thisError;
+     *   setToQuickSum(value, error);
+     * }
+     *
+     * @param otherValue The other value by which to multiply this {@code 
DoubleDouble}.
+     * @param otherError The error of the other value by which to multiply 
this {@code DoubleDouble}.
+     */
+    public void multiply(final double otherValue, final double otherError) {
+        final double thisValue = this.value;
+        final double thisError = this.error;
+        setToProduct(thisValue, otherValue);
+        error += otherError * thisValue;
+        error += otherValue * thisError;
+        normalize();
+    }
+
+    /**
+     * Divides this {@code DoubleDouble} by an other double-double value.
+     * This is a convenience method for:
+     *
+     * {@preformat java
+     *    divide(other.value, other.error);
+     * }
+     *
+     * @param other The other value to add to this value.
+     */
+    public void divide(final DoubleDouble other) {
+        divide(other.value, other.error);
+    }
+
+    /**
+     * Divides this {@code DoubleDouble} by an other double-double value.
+     * The result is stored in this instance.
+     *
+     * @param denominatorValue The other value by which to divide this {@code 
DoubleDouble}.
+     * @param denominatorError The error of the other value by which to divide 
this {@code DoubleDouble}.
+     */
+    public void divide(final double denominatorValue, final double 
denominatorError) {
+        final double numeratorValue = value;
+        final double numeratorError = error;
+        value = denominatorValue;
+        error = denominatorError;
+        inverseDivide(numeratorValue, numeratorError);
+    }
+
+    /**
+     * Divides the given double-double value by this {@code DoubleDouble}.
+     * The result is stored in this instance.
+     *
+     * {@section Implementation}
+     * If <var>a</var> and <var>b</var> are {@code DoubleDouble} instances, 
then we estimate:
+     *
+     *   <blockquote>(a / b) = (a.value / b.value) + remaining / b</blockquote>
+     *
+     * where:
+     *
+     *   <blockquote>remaining = a - b * (a.value / b.value)</blockquote>
+     *
+     * @param numeratorValue The other value to divide by this {@code 
DoubleDouble}.
+     * @param numeratorError The error of the other value to divide by this 
{@code DoubleDouble}.
+     */
+    public void inverseDivide(final double numeratorValue, final double 
numeratorError) {
+        final double denominatorValue = value;
+        /*
+         * The 'b * (a.value / b.value)' part in the method javadoc.
+         */
+        final double quotient = numeratorValue / denominatorValue;
+        multiply(quotient, 0);
+        /*
+         * Compute 'remaining' as 'a - above_product'.
+         */
+        final double productError = error;
+        setToSum(numeratorValue, -value);
+        error -= productError;  // Complete the above subtraction
+        error += numeratorError;
+        /*
+         * Adds the 'remaining / b' term, using 'remaining / b.value' as an 
approximation
+         * (otherwise we would have to invoke this method recursively). The 
approximation
+         * is assumed okay since the second term is small compared to the 
first one.
+         */
+        setToQuickSum(quotient, (value + error) / denominatorValue);
+    }
+
+    /**
+     * Returns a string representation of this number for debugging purpose.
+     * The returned string does not need to contains all digits that this 
{@code DoubleDouble} can handle.
+     *
+     * @return A string representation of this number.
+     */
+    @Override
+    public String toString() {
+        return String.valueOf(value);
+    }
+}

Propchange: 
sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/util/DoubleDouble.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/util/DoubleDouble.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Added: 
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=1526663&view=auto
==============================================================================
--- 
sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/internal/util/DoubleDoubleTest.java
 (added)
+++ 
sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/internal/util/DoubleDoubleTest.java
 [UTF-8] Thu Sep 26 20:02:11 2013
@@ -0,0 +1,298 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.internal.util;
+
+import java.util.Random;
+import java.math.BigDecimal;
+import java.math.MathContext;
+import java.lang.reflect.Field;
+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.*;
+import static java.lang.StrictMath.*;
+
+
+/**
+ * Tests {@link DoubleDouble} using {@link BigDecimal} as the references.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.4
+ * @version 0.4
+ * @module
+ */
+@DependsOn(org.apache.sis.math.MathFunctionsTest.class)
+public final strictfp class DoubleDoubleTest extends TestCase {
+    /**
+     * Number of iterations in each test method.
+     */
+    private static final int NUM_ITERATIONS = 1000;
+
+    /**
+     * The tolerance factor (as a multiplicand) for the addition and 
subtraction operations.
+     */
+    private static final double ADD_TOLERANCE_FACTOR = 1;
+
+    /**
+     * The tolerance factor (as a multiplicand) for the multiplication and 
division operations.
+     * This is a tolerance factor in units of {@link DoubleDouble#error} ULP, 
so even a "scary"
+     * factor like 1E+4 should be very small compared to the {@link 
DoubleDouble#value}.
+     */
+    private static final double PRODUCT_TOLERANCE_FACTOR = 10000;
+
+    /**
+     * The random number generator to use for the test.
+     */
+    private final Random random = TestUtilities.createRandomNumberGenerator();
+
+    /**
+     * Returns the next {@code double} random value. The scale factor is a 
power of two
+     * in order to change only the exponent part of the IEEE representation.
+     */
+    private double nextRandom() {
+        return random.nextDouble() * 2048 - 1024;
+    }
+
+    /**
+     * Fetches the next {@code DoubleDouble} random values and store them in 
the given object.
+     */
+    private void nextRandom(final DoubleDouble dd) {
+        dd.setToSum(nextRandom(), nextRandom());
+    }
+
+    /**
+     * Returns a {@code BigDecimal} representation of the given {@code 
DoubleDouble} value.
+     */
+    private static BigDecimal toBigDecimal(final DoubleDouble dd) {
+        return new BigDecimal(dd.value).add(new BigDecimal(dd.error));
+    }
+
+    /**
+     * Asserts that the given {@code DoubleDouble} is normalized and has a 
value equals to the expected one.
+     * More specifically:
+     *
+     * <ul>
+     *   <li>the {@link DoubleDouble#value} is strictly equals to the expected 
value, and</li>
+     *   <li>the {@link DoubleDouble#error} is not greater than 1 ULP of the 
above value.</li>
+     * </ul>
+     */
+    private static void assertNormalizedAndEquals(final double expected, final 
DoubleDouble actual) {
+        assertTrue("DoubleDouble is not normalized.", abs(actual.error) <= 
ulp(actual.value));
+        assertEquals("Unexpected arithmetic result.", expected, actual.value, 
0.0);
+    }
+
+    /**
+     * Asserts that the result of some operation is equals to the expected 
value,
+     * up to a tolerance value determined by the extended arithmetic precision.
+     *
+     * @param expected The expected value, computed using {@code BigInteger} 
arithmetic.
+     * @param actual The actual value.
+     * @param ef Multiplication factor for the tolerance threshold.
+     */
+    private static void assertExtendedEquals(final BigDecimal expected, final 
DoubleDouble actual, final double ef) {
+        final BigDecimal value = toBigDecimal(actual);
+        final double delta = abs(expected.subtract(value).doubleValue());
+        final double threshold = ulp(actual.error) * ef;
+        if (!(delta <= threshold)) { // Use ! for catching NaN values.
+            fail("Arithmetic error:\n" +
+                 "  Expected:   " + expected  + '\n' +
+                 "  Actual:     " + value     + '\n' +
+                 "  Difference: " + delta     + '\n' +
+                 "  Threshold:  " + threshold + '\n' +
+                 "  Value ULP:  " + ulp(actual.value) + '\n');
+        }
+    }
+
+    /**
+     * Tests {@link DoubleDouble#normalize()}.
+     */
+    @Test
+    @DependsOnMethod("testSetToQuickSum")
+    public void testNormalize() {
+        final DoubleDouble dd = new DoubleDouble();
+        for (int i=0; i<NUM_ITERATIONS; i++) {
+            double a = nextRandom();
+            double b = nextRandom();
+            if (abs(a) < abs(b)) {
+                final double t = a;
+                a = b;
+                b = t;
+            }
+            dd.value = a;
+            dd.error = b;
+            dd.normalize();
+            assertNormalizedAndEquals(a + b, dd);
+            assertExtendedEquals(new BigDecimal(a).add(new BigDecimal(b)), dd, 
ADD_TOLERANCE_FACTOR);
+        }
+    }
+
+    /**
+     * Tests {@link DoubleDouble#setToQuickSum(double, double)}.
+     */
+    @Test
+    public void testSetToQuickSum() {
+        final DoubleDouble dd = new DoubleDouble();
+        for (int i=0; i<NUM_ITERATIONS; i++) {
+            double a = nextRandom();
+            double b = nextRandom();
+            if (abs(a) < abs(b)) {
+                final double t = a;
+                a = b;
+                b = t;
+            }
+            dd.setToQuickSum(a, b);
+            assertNormalizedAndEquals(a + b, dd);
+            assertExtendedEquals(new BigDecimal(a).add(new BigDecimal(b)), dd, 
ADD_TOLERANCE_FACTOR);
+        }
+    }
+
+    /**
+     * Tests {@link DoubleDouble#setToSum(double, double)}.
+     */
+    @Test
+    public void testSetToSum() {
+        final DoubleDouble dd = new DoubleDouble();
+        for (int i=0; i<NUM_ITERATIONS; i++) {
+            final double a = nextRandom();
+            final double b = nextRandom();
+            dd.setToSum(a, b);
+            assertNormalizedAndEquals(a + b, dd);
+            assertExtendedEquals(new BigDecimal(a).add(new BigDecimal(b)), dd, 
ADD_TOLERANCE_FACTOR);
+        }
+    }
+
+    /**
+     * Tests {@link DoubleDouble#setToProduct(double, double)}.
+     */
+    @Test
+    public void testSetToProduct() {
+        final DoubleDouble dd = new DoubleDouble();
+        for (int i=0; i<NUM_ITERATIONS; i++) {
+            final double a = nextRandom();
+            final double b = nextRandom();
+            dd.setToProduct(a, b);
+            assertNormalizedAndEquals(a * b, dd);
+            assertExtendedEquals(new BigDecimal(a).multiply(new 
BigDecimal(b)), dd, ADD_TOLERANCE_FACTOR);
+        }
+    }
+
+    /**
+     * Tests {@link DoubleDouble#add(DoubleDouble)}.
+     */
+    @Test
+    @DependsOnMethod({"testSetToSum", "testNormalize"})
+    public void testAdd() {
+        final DoubleDouble dd = new DoubleDouble();
+        final DoubleDouble op = new DoubleDouble();
+        for (int i=0; i<NUM_ITERATIONS; i++) {
+            nextRandom(dd);
+            nextRandom(op);
+            final BigDecimal expected = toBigDecimal(dd).add(toBigDecimal(op));
+            dd.add(op); // Must be after 'expected' computation.
+            assertExtendedEquals(expected, dd, ADD_TOLERANCE_FACTOR);
+        }
+    }
+
+    /**
+     * Tests {@link DoubleDouble#multiply(DoubleDouble)}.
+     */
+    @Test
+    @DependsOnMethod({"testSetToProduct", "testNormalize"})
+    public void testMultiply() {
+        final DoubleDouble dd = new DoubleDouble();
+        final DoubleDouble op = new DoubleDouble();
+        for (int i=0; i<NUM_ITERATIONS; i++) {
+            nextRandom(dd);
+            nextRandom(op);
+            final BigDecimal expected = 
toBigDecimal(dd).multiply(toBigDecimal(op), MathContext.DECIMAL128);
+            dd.multiply(op); // Must be after 'expected' computation.
+            assertExtendedEquals(expected, dd, PRODUCT_TOLERANCE_FACTOR);
+        }
+    }
+
+    /**
+     * Tests {@link DoubleDouble#divide(DoubleDouble)}.
+     */
+    @Test
+    @DependsOnMethod({"testMultiply", "testSetToSum", "testSetToQuickSum"})
+    public void testDivide() {
+        final DoubleDouble dd = new DoubleDouble();
+        final DoubleDouble op = new DoubleDouble();
+        for (int i=0; i<NUM_ITERATIONS; i++) {
+            nextRandom(dd);
+            nextRandom(op);
+            final BigDecimal expected = 
toBigDecimal(dd).divide(toBigDecimal(op), MathContext.DECIMAL128);
+            dd.divide(op); // Must be after 'expected' computation.
+            assertExtendedEquals(expected, dd, PRODUCT_TOLERANCE_FACTOR);
+        }
+    }
+
+    /**
+     * List of all {@link DoubleDouble#VALUES} as string decimal 
representation.
+     */
+    private static final String[] PREDEFINED_VALUES = {
+        "0.000001",
+        "0.00001",
+        "0.0001",
+        "0.001",
+        "0.01",
+        "0.1",
+        "0.3048",
+        "0.9"
+    };
+
+    /**
+     * Ensures that the {@link DoubleDouble#VALUES} elements are sorted in 
strictly increasing order.
+     * Also ensures that {@link DoubleDouble#ERRORS} has the same number of 
elements.
+     *
+     * @throws ReflectiveOperationException Should never happen.
+     */
+    @Test
+    public void testValuesSorted() throws ReflectiveOperationException {
+        Field field = DoubleDouble.class.getDeclaredField("VALUES");
+        field.setAccessible(true);
+        double[] array = (double[]) field.get(null);
+        assertTrue(ArraysExt.isSorted(array, true));
+        assertEquals(PREDEFINED_VALUES.length, array.length);
+
+        field = DoubleDouble.class.getDeclaredField("ERRORS");
+        field.setAccessible(true);
+        array = (double[]) field.get(null);
+        assertEquals(PREDEFINED_VALUES.length, array.length);
+    }
+
+    /**
+     * Tests {@link DoubleDouble#errorForWellKnownValue(double)}.
+     */
+    @Test
+    @DependsOnMethod("testValuesSorted")
+    public void testErrorForWellKnownValue() {
+        for (final String text : PREDEFINED_VALUES) {
+            final double     value         = Double.valueOf(text);
+            final BigDecimal accurate      = new BigDecimal(text);
+            final BigDecimal approximation = new BigDecimal(value);
+            final double     expected      = 
accurate.subtract(approximation).doubleValue();
+            assertEquals(text,  expected, DoubleDouble.errorForWellKnownValue( 
value), 0);
+            assertEquals(text, -expected, 
DoubleDouble.errorForWellKnownValue(-value), 0);
+            assertFalse("There is no point to define an entry for values 
having no error.", expected == 0);
+        }
+    }
+}

Propchange: 
sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/internal/util/DoubleDoubleTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/internal/util/DoubleDoubleTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

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=1526663&r1=1526662&r2=1526663&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] Thu Sep 26 20:02:11 2013
@@ -58,6 +58,7 @@ import org.junit.BeforeClass;
     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.


Reply via email to