Author: desruisseaux
Date: Fri Oct 4 09:15:14 2013
New Revision: 1529105
URL: http://svn.apache.org/r1529105
Log:
Safety against some rounding errors.
Modified:
sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/GeneralMatrix.java
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/Matrix4Test.java
sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/util/DoubleDouble.java
Modified:
sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/GeneralMatrix.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/GeneralMatrix.java?rev=1529105&r1=1529104&r2=1529105&view=diff
==============================================================================
---
sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/GeneralMatrix.java
[UTF-8] (original)
+++
sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/GeneralMatrix.java
[UTF-8] Fri Oct 4 09:15:14 2013
@@ -295,6 +295,12 @@ class GeneralMatrix extends MatrixSIS {
/**
* {@inheritDoc}
+ *
+ * <p>This method does not check the error terms, because those terms are
not visible to the user
+ * (they can not appear in the value returned by {@link #getElement(int,
int)}, and are not shown
+ * by {@link #toString()}) - returning {@code false} while the matrix
clearly looks like affine
+ * would be confusing for the user. Furthermore, the errors can be
non-zero only in the very last
+ * element and that value always smaller than 2.3E-16.</p>
*/
@Override
public final boolean isAffine() {
@@ -309,11 +315,7 @@ class GeneralMatrix extends MatrixSIS {
return false;
}
}
- /*
- * At this point, the 'double' values are those of an affine
transform.
- * If this matrix uses extended precision, ensures that their
errors are zero.
- */
- return errorsAreZero(base + numRow*numCol);
+ return true;
}
}
return false;
@@ -321,6 +323,16 @@ class GeneralMatrix extends MatrixSIS {
/**
* {@inheritDoc}
+ *
+ * <p>This method does not check the error terms, because those terms are
not visible to the user
+ * (they can not appear in the value returned by {@link #getElement(int,
int)}, and are not shown
+ * by {@link #toString()}) - returning {@code false} while the matrix
clearly looks like identity
+ * would be confusing for the user. Furthermore, the errors can be
non-zero only on the diagonal,
+ * and those values always smaller than 2.3E-16.</p>
+ *
+ * <p>An other argument is that the extended precision is for reducing
rounding errors during
+ * matrix arithmetics. But since the user provided the original data as
{@code double} values,
+ * the extra precision usually have no "real" meaning.</p>
*/
@Override
public final boolean isIdentity() {
@@ -340,25 +352,6 @@ class GeneralMatrix extends MatrixSIS {
if (element != 0) return false;
}
}
- /*
- * At this point, the 'double' values are those of an identity
transform.
- * If this matrix uses extended precision, ensures that all errors are
zero.
- */
- return errorsAreZero(length);
- }
-
- /**
- * Returns {@code true} if this matrix has no error elements, or if all
error elements starting
- * at the given index are zero.
- *
- * @param i Index of the first error elements to check (may be greater
than the array length).
- */
- private boolean errorsAreZero(int i) {
- while (i < elements.length) {
- if (elements[i++] != 0) {
- return false;
- }
- }
return true;
}
Modified:
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/Matrix4Test.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/Matrix4Test.java?rev=1529105&r1=1529104&r2=1529105&view=diff
==============================================================================
---
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/Matrix4Test.java
[UTF-8] (original)
+++
sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/Matrix4Test.java
[UTF-8] Fri Oct 4 09:15:14 2013
@@ -115,12 +115,10 @@ public final strictfp class Matrix4Test
* We have a slight residu in the longitude translation term because
of the
* prime meridian shift - we will set this residu to zero for this
test.
*
- * Note that the 'isIdentity(0)' test fail if the double-double
arithmetic is
+ * Note that the 'isIdentity()' test fail if the double-double
arithmetic is
* disabled, because some scale factors will be 0.9999999999999999
instead of 1.
*/
final MatrixSIS result = step3.multiply(step2).multiply(step1);
- assertEquals("translateX", 0, result.getElement(0,3), 1E-32);
- result.setElement(0,3, 0);
- assertTrue("isIdentity(0)", Matrices.isIdentity(result, 0));
+ assertTrue("isIdentity()", result.isIdentity());
}
}
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=1529105&r1=1529104&r2=1529105&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 Oct 4 09:15:14 2013
@@ -421,6 +421,20 @@ public final class DoubleDouble extends
value += otherValue;
error += v - (value + (v -= value)) + (otherValue + v);
error += otherError;
+ if (value == 0 && error != 0) {
+ /*
+ * The two values almost cancelled, only their error terms are
different.
+ * 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 50 additional
+ * significand bits (we ignore 2 bits as a safety margin). 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), -50)) {
+ error = 0;
+ return;
+ }
+ }
normalize();
}
@@ -622,11 +636,11 @@ public final class DoubleDouble extends
* {@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>
+ * <blockquote>(a / b) = (a.value / b.value) + remainder / b</blockquote>
*
* where:
*
- * <blockquote>remaining = a - b * (a.value / b.value)</blockquote>
+ * <blockquote>remainder = 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}.
@@ -644,14 +658,14 @@ public final class DoubleDouble extends
final double quotient = numeratorValue / denominatorValue;
multiply(quotient, 0);
/*
- * Compute 'remaining' as 'a - above_product'.
+ * Compute 'remainder' 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
+ * Adds the 'remainder / b' term, using 'remainder / 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.
*/