sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1401576 - in /sis/branches/JDK7/sis-utility/src: main/java/org/apache/sis/math/MathFunctions.java test/java/org/apache/sis/math/MathFunctionsTest.java
Date Wed, 24 Oct 2012 08:02:51 GMT
Author: desruisseaux
Date: Wed Oct 24 08:02:50 2012
New Revision: 1401576

URL: http://svn.apache.org/viewvc?rev=1401576&view=rev
Log:
Added a function for computing the number of digits needed for formatting numbers with a given
accuracy.
This will be used for formatting Angle with the necessary number of digits.

Modified:
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/math/MathFunctions.java
    sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/math/MathFunctionsTest.java

Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/math/MathFunctions.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/math/MathFunctions.java?rev=1401576&r1=1401575&r2=1401576&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/math/MathFunctions.java (original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/math/MathFunctions.java Wed
Oct 24 08:02:50 2012
@@ -163,7 +163,7 @@ public final class MathFunctions extends
      *         or less in magnitude than the given value.
      */
     public static double truncate(final double value) {
-        return (value < 0) ? Math.ceil(value) : Math.floor(value);
+        return (doubleToRawLongBits(value) & SIGN_BIT_MASK) == 0 ? Math.floor(value)
: Math.ceil(value);
     }
 
     /**
@@ -215,6 +215,85 @@ public final class MathFunctions extends
     }
 
     /**
+     * Returns the number of fraction digits needed for formatting in base 10 numbers of
the given
+     * accuracy. For any value {@code accuracy}, this method returns a value <var>n</var>
such as
+     * the difference between adjacent numbers formatted in base 10 with <var>n</var>
fraction
+     * digits will always be equals or smaller than {@code accuracy}. Examples:
+     *
+     * <ul>
+     *   <li>{@code fractionDigitsForDelta(0.001)} returns 3.</li>
+     *   <li>{@code fractionDigitsForDelta(0.009)} returns 3.</li>
+     *   <li>{@code fractionDigitsForDelta(0.010)} returns 2.</li>
+     * </ul>
+     *
+     * <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 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>
+     *   <li>If the first non-zero digits of {@code accuracy} are equal or greater
than 95
+     *       (e.g. 0.00099), 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.
+     *
+     *       <blockquote><font size="-1"><b>Example:</b>
+     *       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
+     *       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.
+     *       </font></blockquote></li>
+     * </ul>
+     *
+     * <p>Invoking this method is equivalent to computing <code>(int)
+     * -{@linkplain Math#floor(double) floor}({@linkplain Math#log10(double) log10}(accuracy))</code>
+     * except for the 0, {@code NaN}, infinities and 0.…95 special cases.</p>
+     *
+     * @param  accuracy The desired accuracy of numbers to format in base 10.
+     * @return Number of fraction digits needed for formatting numbers with the given accuracy.
+     *         May be negative.
+     *
+     * @see java.text.NumberFormat#setMaximumFractionDigits(int)
+     */
+    public static int fractionDigitsForDelta(double accuracy) {
+        accuracy = Math.abs(accuracy);
+        final boolean isFraction = (accuracy < 1);
+        /*
+         * Compute (int) Math.log10(x) with opportunist use of the POW10 array.
+         * We use the POW10 array because accurate calculation of log10 is relatively costly,
+         * while we only want the integer part. A micro-benchmarking on JDK7 suggested that
a
+         * binary search on POW10 is about 30% faster than invoking (int) Math.log10(x).
+         */
+        int i = Arrays.binarySearch(POW10, isFraction ? 1/accuracy : accuracy);
+        if (i >= 0) {
+            return isFraction ? i : -i;
+        }
+        i = ~i;
+        double scale;
+        if (i < POW10.length) {
+            scale = POW10[i];
+            if (!isFraction) {
+                i = -(i-1);
+                scale = 10 / scale;
+            }
+        } else { // 'x' is out of range or NaN.
+            final double y = Math.log10(accuracy);
+            if (Double.isInfinite(y)) {
+                return 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;
+        }
+        return i;
+    }
+
+    /**
      * 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>

Modified: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/math/MathFunctionsTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/math/MathFunctionsTest.java?rev=1401576&r1=1401575&r2=1401576&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/math/MathFunctionsTest.java
(original)
+++ sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/math/MathFunctionsTest.java
Wed Oct 24 08:02:50 2012
@@ -52,6 +52,27 @@ public final strictfp class MathFunction
     private static final int LOWEST_USHORT_PRIME = 32771;
 
     /**
+     * Tests {@link MathFunctions#truncate(double)}.
+     */
+    @Test
+    public void testTruncate() {
+        assertEquals(+4, truncate(+4.9), 0);
+        assertEquals(-4, truncate(-4.9), 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)));
+    }
+
+    /**
      * Tests the {@link MathFunctions#magnitude(double[])} method.
      */
     @Test
@@ -63,6 +84,54 @@ public final strictfp class MathFunction
     }
 
     /**
+     * Tests {@link #fractionDigitsForDelta(double)}.
+     */
+    @Test
+    public void testFractionDigitsForDelta() {
+        assertEquals(3, fractionDigitsForDelta(0.001));
+        assertEquals(3, fractionDigitsForDelta(0.009));
+        assertEquals(2, fractionDigitsForDelta(0.010));
+        assertEquals(2, fractionDigitsForDelta(0.015));
+        assertEquals(1, fractionDigitsForDelta(0.100));
+        assertEquals(1, fractionDigitsForDelta(0.125));
+        assertEquals(1, fractionDigitsForDelta(0.949));
+        assertEquals(2, fractionDigitsForDelta(0.994)); // Special case
+        assertEquals(3, fractionDigitsForDelta(0.999)); // Special case
+
+        assertEquals( 0, fractionDigitsForDelta(  1.0));
+        assertEquals( 0, fractionDigitsForDelta(  1.9));
+        assertEquals( 0, fractionDigitsForDelta(  9.1));
+        assertEquals(-1, fractionDigitsForDelta( 10.0));
+        assertEquals(-1, fractionDigitsForDelta( 19.9));
+        assertEquals(-1, fractionDigitsForDelta( 94.9));
+        assertEquals( 0, fractionDigitsForDelta( 99.0)); // Special case
+        assertEquals(-2, fractionDigitsForDelta(100.0));
+        assertEquals(-2, fractionDigitsForDelta(100.1));
+        assertEquals(-1, fractionDigitsForDelta(994.9)); // Special case
+        assertEquals(+1, fractionDigitsForDelta(999.9)); // Special case
+        assertEquals(-3, fractionDigitsForDelta(1000));
+
+        // Tests values out of the POW10 array range.
+        assertEquals(23,  fractionDigitsForDelta(1.0E-23));
+        assertEquals(23,  fractionDigitsForDelta(1.9E-23));
+        assertEquals(23,  fractionDigitsForDelta(9.1E-23));
+        assertEquals(24,  fractionDigitsForDelta(9.6E-23)); // Special case
+        assertEquals(300, fractionDigitsForDelta(1.1E-300));
+
+        assertEquals(-23,  fractionDigitsForDelta(1.0E+23));
+        assertEquals(-23,  fractionDigitsForDelta(1.9E+23));
+        assertEquals(-23,  fractionDigitsForDelta(9.1E+23));
+        assertEquals(-22,  fractionDigitsForDelta(9.6E+23)); // Special case
+        assertEquals(-300, fractionDigitsForDelta(1.1E+300));
+
+        // Special cases.
+        assertEquals(0,  fractionDigitsForDelta(0));
+        assertEquals(0,  fractionDigitsForDelta(Double.NaN));
+        assertEquals(0,  fractionDigitsForDelta(Double.POSITIVE_INFINITY));
+        assertEquals(0,  fractionDigitsForDelta(Double.NEGATIVE_INFINITY));
+    }
+
+    /**
      * Tests the {@link MathFunctions#pow10(double)} method.
      * This will indirectly test {@link MathFunctions#pow10(int)}
      * since the former will delegate to the later in this test.



Mime
View raw message