sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1538118 - in /sis/branches/JDK7/core/sis-utility/src: main/java/org/apache/sis/math/DecimalFunctions.java test/java/org/apache/sis/math/DecimalFunctionsTest.java
Date Sat, 02 Nov 2013 01:03:35 GMT
Author: desruisseaux
Date: Sat Nov  2 01:03:35 2013
New Revision: 1538118

URL: http://svn.apache.org/r1538118
Log:
Added an utility method for widening conversions of values parsed from an ASCII file.

Modified:
    sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/math/DecimalFunctions.java
    sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/math/DecimalFunctionsTest.java

Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/math/DecimalFunctions.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/math/DecimalFunctions.java?rev=1538118&r1=1538117&r2=1538118&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/math/DecimalFunctions.java
[UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/math/DecimalFunctions.java
[UTF-8] Sat Nov  2 01:03:35 2013
@@ -65,6 +65,75 @@ public final class DecimalFunctions exte
     }
 
     /**
+     * Converts the given {@code float} value to a {@code double} with the extra <em>decimal</em>
fraction digits
+     * set to zero. This is different than the standard cast in the Java language, which
set the extra <em>binary</em>
+     * fraction digits to zero.
+     * For example {@code (double) 0.1f} gives 0.10000000149011612 while {@code convert(0.1f)}
returns 0.1.
+     *
+     * {@note This method is <strong>not</strong> more accurate than the standard
Java cast —
+     *        it is only more intuitive for human used to base 10.
+     *        If the value come directly from an ASCII file or a user input, then this method
may be useful
+     *        because the value was probably expressed in base 10 before conversion to a
{@code float}.
+     *        But if the value come from an instrument measurement or a calculation, then
there is probably
+     *        no reason to use this method because base 10 is not more "real" than base 2
or any other base
+     *        for natural phenomenon.}
+     *
+     * This method is equivalent to the following code, except that it is potentially faster
since the
+     * actual implementation avoid to format and parse the value:
+     *
+     * {@preformat java
+     *   return Double.parseDouble(Float.toString(value));
+     * }
+     *
+     * @param  value The {@code float} value to convert as a {@code double}.
+     * @return The given value as a {@code double} with the extra decimal fraction digits
set to zero.
+     */
+    public static double convert(final float value) {
+        if (Float.isNaN(value) || Float.isInfinite(value) || value == 0f) {
+            return value;
+        }
+        /*
+         * Decompose  value == m × 2^e  where m and e are integers. If the exponent is not
negative, then
+         * there is no fractional part in the value, in which case there is no rounding error
to fix.
+         */
+        final int e = Math.getExponent(value) - Numerics.SIGNIFICAND_SIZE_OF_FLOAT;
+        if (e >= 0) {
+            return value;
+        }
+        final int m = Numerics.getSignificand(value);
+        assert Math.scalb((float) m, e) == value : value;
+        /*
+         * Get the factor for converting the significand from base 2 to base 10, such as:
+         *
+         *    m × (2^e)  ==  m × c × (10 ^ -e10)
+         *
+         * where e10 is the smallest exponent which allow to represent the value without
precision lost when (m × c)
+         * is rounded to an integer. Because the number of significant digits in base 2 does
not correspond to an
+         * integer number of significand digits in base 10, we have slightly more precision
than what the 'float'
+         * value had: we have something between 0 and 1 extraneous digits.
+         */
+        final int    e10 = -Numerics.toExp10(e);      // Make positive
+        final double c   = Math.scalb(pow10(e10), e); // Conversion factor, also 1 ULP in
base 10.
+        final double mc  = m * c;
+        /*
+         * First, presume that our representation in base 10 has one extranous digit, so
we will round
+         * to the tens instead of unities. If the difference appears to not be smaller than
half a ULP,
+         * then the last digit was not extranous - we need to keep it.
+         */
+        double delta = Math.rint(mc/10)*10 - mc;
+        if (Math.abs(delta) >= c/2) {
+            delta = Math.rint(mc) - mc;
+        }
+        /*
+         * Now compute the final adjustment that we need to apply to the value.
+         * This adjustment shall always be lower then half a ULP.
+         */
+        delta = Math.scalb(delta / c, e);
+        assert Math.abs(delta) < Math.ulp(value) / 2 : value;
+        return value + delta;
+    }
+
+    /**
      * Returns the number of fraction digits needed for formatting in base 10 numbers of
the given
      * accuracy. If the {@code strict} argument is {@code true}, then for any given {@code
accuracy}
      * this method returns a value <var>n</var> such as the difference between
adjacent numbers

Modified: sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/math/DecimalFunctionsTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/math/DecimalFunctionsTest.java?rev=1538118&r1=1538117&r2=1538118&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/math/DecimalFunctionsTest.java
[UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/math/DecimalFunctionsTest.java
[UTF-8] Sat Nov  2 01:03:35 2013
@@ -56,6 +56,20 @@ public final strictfp class DecimalFunct
     }
 
     /**
+     * Tests {@link DecimalFunctions#convert(float)}.
+     */
+    @Test
+    public void testConvertAsDecimal() {
+        assertEquals(10,     convert(10f),     0);
+        assertEquals(0.1,    convert(0.1f),    0);
+        assertEquals(0.01,   convert(0.01f),   0);
+        assertEquals(0.001,  convert(0.001f),  0);
+        assertEquals(0.0001, convert(0.0001f), 0);
+        assertEquals(3.7E-8, convert(3.7E-8f), 0);
+//      assertEquals(3.7E-9, convert(3.7E-9f), 0);
+    }
+
+    /**
      * Tests {@link DecimalFunctions#fractionDigitsForDelta(double, boolean)}.
      */
     @Test



Mime
View raw message