commons-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From l..@apache.org
Subject svn commit: r1062549 - in /commons/proper/math/branches/MATH_2_X/src: main/java/org/apache/commons/math/util/ site/xdoc/ test/java/org/apache/commons/math/util/
Date Sun, 23 Jan 2011 22:19:38 GMT
Author: luc
Date: Sun Jan 23 22:19:37 2011
New Revision: 1062549

URL: http://svn.apache.org/viewvc?rev=1062549&view=rev
Log:
added FastMath.scalb(double, int) and FastMath.scalb(float, int)
deprecated MathUtils.scalb(double, int)
JIRA: MATH-498

Modified:
    commons/proper/math/branches/MATH_2_X/src/main/java/org/apache/commons/math/util/FastMath.java
    commons/proper/math/branches/MATH_2_X/src/main/java/org/apache/commons/math/util/MathUtils.java
    commons/proper/math/branches/MATH_2_X/src/site/xdoc/changes.xml
    commons/proper/math/branches/MATH_2_X/src/test/java/org/apache/commons/math/util/FastMathTest.java
    commons/proper/math/branches/MATH_2_X/src/test/java/org/apache/commons/math/util/MathUtilsTest.java

Modified: commons/proper/math/branches/MATH_2_X/src/main/java/org/apache/commons/math/util/FastMath.java
URL: http://svn.apache.org/viewvc/commons/proper/math/branches/MATH_2_X/src/main/java/org/apache/commons/math/util/FastMath.java?rev=1062549&r1=1062548&r2=1062549&view=diff
==============================================================================
--- commons/proper/math/branches/MATH_2_X/src/main/java/org/apache/commons/math/util/FastMath.java
(original)
+++ commons/proper/math/branches/MATH_2_X/src/main/java/org/apache/commons/math/util/FastMath.java
Sun Jan 23 22:19:37 2011
@@ -3401,6 +3401,162 @@ public class FastMath {
     }
 
     /**
+     * Multiply a double number by a power of 2.
+     * @param d number to multiply
+     * @param n power of 2
+     * @return d &times; 2<sup>n</sup>
+     */
+    public static double scalb(final double d, final int n) {
+
+        // first simple and fast handling when 2^n can be represented using normal numbers
+        if ((n > -1023) && (n < 1024)) {
+            return d * Double.longBitsToDouble(((long) (n + 1023)) << 52);
+        }
+
+        // handle special cases
+        if (Double.isNaN(d) || Double.isInfinite(d) || (d == 0)) {
+            return d;
+        }
+
+        // decompose d
+        final long bits = Double.doubleToLongBits(d);
+        final long sign = bits & 0x8000000000000000L;
+        int  exponent   = ((int) (bits >>> 52)) & 0x7ff;
+        long mantissa   = bits & 0x000fffffffffffffL;
+
+        // compute scaled exponent
+        int scaledExponent = exponent + n;
+
+        if (n < 0) {
+            // we are really in the case n <= -1023
+            if (scaledExponent > 0) {
+                // both the input and the result are normal numbers, we only adjust the exponent
+                return Double.longBitsToDouble(sign | (((long) scaledExponent) << 52)
| mantissa);
+            } else if (scaledExponent > -53) {
+                // the input is a normal number and the result is a subnormal number
+
+                // recover the hidden mantissa bit
+                mantissa = mantissa | (1L << 52);
+
+                // scales down complete mantissa, hence losing least significant bits
+                final long mostSignificantLostBit = mantissa & (1L << (-scaledExponent));
+                mantissa = mantissa >>> (1 - scaledExponent);
+                if (mostSignificantLostBit != 0) {
+                    // we need to add 1 bit to round up the result
+                    mantissa++;
+                }
+                return Double.longBitsToDouble(sign | mantissa);
+
+            } else {
+                // no need to compute the mantissa, the number scales down to 0
+                return (sign == 0L) ? 0.0 : -0.0;
+            }
+        } else {
+            // we are really in the case n >= 1024
+            if (exponent == 0) {
+
+                // the input number is subnormal, normalize it
+                while ((mantissa >>> 52) != 1) {
+                    mantissa = mantissa << 1;
+                    --scaledExponent;
+                }
+                ++scaledExponent;
+                mantissa = mantissa & 0x000fffffffffffffL;
+
+                if (scaledExponent < 2047) {
+                    return Double.longBitsToDouble(sign | (((long) scaledExponent) <<
52) | mantissa);
+                } else {
+                    return (sign == 0L) ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY;
+                }
+
+            } else if (scaledExponent < 2047) {
+                return Double.longBitsToDouble(sign | (((long) scaledExponent) << 52)
| mantissa);
+            } else {
+                return (sign == 0L) ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY;
+            }
+        }
+
+    }
+
+    /**
+     * Multiply a float number by a power of 2.
+     * @param f number to multiply
+     * @param n power of 2
+     * @return f &times; 2<sup>n</sup>
+     */
+    public static float scalb(final float f, final int n) {
+
+        // first simple and fast handling when 2^n can be represented using normal numbers
+        if ((n > -127) && (n < 128)) {
+            return f * Float.intBitsToFloat((n + 127) << 23);
+        }
+
+        // handle special cases
+        if (Float.isNaN(f) || Float.isInfinite(f) || (f == 0f)) {
+            return f;
+        }
+
+        // decompose f
+        final int bits = Float.floatToIntBits(f);
+        final int sign = bits & 0x80000000;
+        int  exponent  = (bits >>> 23) & 0xff;
+        int mantissa   = bits & 0x007fffff;
+
+        // compute scaled exponent
+        int scaledExponent = exponent + n;
+
+        if (n < 0) {
+            // we are really in the case n <= -127
+            if (scaledExponent > 0) {
+                // both the input and the result are normal numbers, we only adjust the exponent
+                return Float.intBitsToFloat(sign | (scaledExponent << 23) | mantissa);
+            } else if (scaledExponent > -24) {
+                // the input is a normal number and the result is a subnormal number
+
+                // recover the hidden mantissa bit
+                mantissa = mantissa | (1 << 23);
+
+                // scales down complete mantissa, hence losing least significant bits
+                final int mostSignificantLostBit = mantissa & (1 << (-scaledExponent));
+                mantissa = mantissa >>> (1 - scaledExponent);
+                if (mostSignificantLostBit != 0) {
+                    // we need to add 1 bit to round up the result
+                    mantissa++;
+                }
+                return Float.intBitsToFloat(sign | mantissa);
+
+            } else {
+                // no need to compute the mantissa, the number scales down to 0
+                return (sign == 0) ? 0.0f : -0.0f;
+            }
+        } else {
+            // we are really in the case n >= 128
+            if (exponent == 0) {
+
+                // the input number is subnormal, normalize it
+                while ((mantissa >>> 23) != 1) {
+                    mantissa = mantissa << 1;
+                    --scaledExponent;
+                }
+                ++scaledExponent;
+                mantissa = mantissa & 0x007fffff;
+
+                if (scaledExponent < 255) {
+                    return Float.intBitsToFloat(sign | (scaledExponent << 23) | mantissa);
+                } else {
+                    return (sign == 0) ? Float.POSITIVE_INFINITY : Float.NEGATIVE_INFINITY;
+                }
+
+            } else if (scaledExponent < 255) {
+                return Float.intBitsToFloat(sign | (scaledExponent << 23) | mantissa);
+            } else {
+                return (sign == 0) ? Float.POSITIVE_INFINITY : Float.NEGATIVE_INFINITY;
+            }
+        }
+
+    }
+
+    /**
      * Get the next machine representable number after a number, moving
      * in the direction of another number.
      * <p>

Modified: commons/proper/math/branches/MATH_2_X/src/main/java/org/apache/commons/math/util/MathUtils.java
URL: http://svn.apache.org/viewvc/commons/proper/math/branches/MATH_2_X/src/main/java/org/apache/commons/math/util/MathUtils.java?rev=1062549&r1=1062548&r2=1062549&view=diff
==============================================================================
--- commons/proper/math/branches/MATH_2_X/src/main/java/org/apache/commons/math/util/MathUtils.java
(original)
+++ commons/proper/math/branches/MATH_2_X/src/main/java/org/apache/commons/math/util/MathUtils.java
Sun Jan 23 22:19:37 2011
@@ -1301,26 +1301,14 @@ public final class MathUtils {
      * <p>If <code>d</code> is 0 or NaN or Infinite, it is returned unchanged.</p>
      *
      * @param d base number
-     * @param scaleFactor power of two by which d sould be multiplied
+     * @param scaleFactor power of two by which d should be multiplied
      * @return d &times; 2<sup>scaleFactor</sup>
      * @since 2.0
+     * @deprecated as of 2.2, replaced by {@link FastMath#scalb(double, int)}
      */
+    @Deprecated
     public static double scalb(final double d, final int scaleFactor) {
-
-        // handling of some important special cases
-        if ((d == 0) || Double.isNaN(d) || Double.isInfinite(d)) {
-            return d;
-        }
-
-        // split the double in raw components
-        final long bits     = Double.doubleToLongBits(d);
-        final long exponent = bits & 0x7ff0000000000000L;
-        final long rest     = bits & 0x800fffffffffffffL;
-
-        // shift the exponent
-        final long newBits = rest | (exponent + (((long) scaleFactor) << 52));
-        return Double.longBitsToDouble(newBits);
-
+        return FastMath.scalb(d, scaleFactor);
     }
 
     /**

Modified: commons/proper/math/branches/MATH_2_X/src/site/xdoc/changes.xml
URL: http://svn.apache.org/viewvc/commons/proper/math/branches/MATH_2_X/src/site/xdoc/changes.xml?rev=1062549&r1=1062548&r2=1062549&view=diff
==============================================================================
--- commons/proper/math/branches/MATH_2_X/src/site/xdoc/changes.xml (original)
+++ commons/proper/math/branches/MATH_2_X/src/site/xdoc/changes.xml Sun Jan 23 22:19:37 2011
@@ -52,15 +52,19 @@ The <action> type attribute can be add,u
     If the output is not quite correct, check for invisible trailing spaces!
      -->
     <release version="2.2" date="TBD" description="TBD">
+      <action dev="luc" type="fix" issue="MATH-498">
+          FastMath is not an exact replacement for StrictMath
+          (partially fixed) added scalb(double, int), scalb(float, int)
+      </action>
       <action dev="luc" type="fix" issue="MATH-478">
           FastMath is not an exact replacement for StrictMath
-          (partially fixed) fixed nextAfter(double, double) and nextAfter(float,double)
+          (partially fixed) added nextAfter(double, double) and nextAfter(float,double)
           (beware of the strange double second argument) so that they handle
           special values in the way as StrictMath
       </action>
       <action dev="luc" type="fix" issue="MATH-497">
           FastMath is not an exact replacement for StrictMath
-          (partially fixed) Add getExponent(double), getExponent(float)
+          (partially fixed) added getExponent(double) and getExponent(float)
       </action>
       <action dev="sebb" type="fix" issue="MATH-496">
           FastMath is not an exact replacement for StrictMath

Modified: commons/proper/math/branches/MATH_2_X/src/test/java/org/apache/commons/math/util/FastMathTest.java
URL: http://svn.apache.org/viewvc/commons/proper/math/branches/MATH_2_X/src/test/java/org/apache/commons/math/util/FastMathTest.java?rev=1062549&r1=1062548&r2=1062549&view=diff
==============================================================================
--- commons/proper/math/branches/MATH_2_X/src/test/java/org/apache/commons/math/util/FastMathTest.java
(original)
+++ commons/proper/math/branches/MATH_2_X/src/test/java/org/apache/commons/math/util/FastMathTest.java
Sun Jan 23 22:19:37 2011
@@ -1066,6 +1066,40 @@ public class FastMathTest {
         Assert.assertEquals(0F, FastMath.nextAfter(-Float.MIN_VALUE, 1F), 0F);
     }
 
+    @Test
+    public void testDoubleScalbSpecialCases() {
+        Assert.assertEquals(2.5269841324701218E-175,  FastMath.scalb(2.2250738585072014E-308,
442), 0D);
+        Assert.assertEquals(1.307993905256674E297,    FastMath.scalb(1.1102230246251565E-16,
1040), 0D);
+        Assert.assertEquals(7.2520887996488946E-217,  FastMath.scalb(Double.MIN_VALUE,  
     356), 0D);
+        Assert.assertEquals(8.98846567431158E307,     FastMath.scalb(Double.MIN_VALUE,  
    2097), 0D);
+        Assert.assertEquals(Double.POSITIVE_INFINITY, FastMath.scalb(Double.MIN_VALUE,  
    2098), 0D);
+        Assert.assertEquals(1.1125369292536007E-308,  FastMath.scalb(2.225073858507201E-308,
  -1), 0D);
+        Assert.assertEquals(1.0E-323,                 FastMath.scalb(Double.MAX_VALUE,  
   -2097), 0D);
+        Assert.assertEquals(Double.MIN_VALUE,         FastMath.scalb(Double.MAX_VALUE,  
   -2098), 0D);
+        Assert.assertEquals(0,                        FastMath.scalb(Double.MAX_VALUE,  
   -2099), 0D);
+        Assert.assertEquals(Double.POSITIVE_INFINITY, FastMath.scalb(Double.POSITIVE_INFINITY,
-1000000), 0D);
+        Assert.assertEquals(Double.NEGATIVE_INFINITY, FastMath.scalb(-1.1102230246251565E-16,
1078), 0D);
+        Assert.assertEquals(Double.NEGATIVE_INFINITY, FastMath.scalb(-1.1102230246251565E-16,
 1079), 0D);
+        Assert.assertEquals(Double.NEGATIVE_INFINITY, FastMath.scalb(-2.2250738585072014E-308,
2047), 0D);
+        Assert.assertEquals(Double.NEGATIVE_INFINITY, FastMath.scalb(-2.2250738585072014E-308,
2048), 0D);
+    }
+
+    @Test
+    public void testFloatScalbSpecialCases() {
+        Assert.assertEquals(0f,                       FastMath.scalb(Float.MIN_VALUE,  -30),
0F);
+        Assert.assertEquals(2 * Float.MIN_VALUE,      FastMath.scalb(Float.MIN_VALUE,   
1), 0F);
+        Assert.assertEquals(7.555786e22f,             FastMath.scalb(Float.MAX_VALUE,  -52),
0F);
+        Assert.assertEquals(1.7014118e38f,            FastMath.scalb(Float.MIN_VALUE,  276),
0F);
+        Assert.assertEquals(Float.POSITIVE_INFINITY,  FastMath.scalb(Float.MIN_VALUE,  277),
0F);
+        Assert.assertEquals(5.8774718e-39f,           FastMath.scalb(1.1754944e-38f,    -1),
0F);
+        Assert.assertEquals(2 * Float.MIN_VALUE,      FastMath.scalb(Float.MAX_VALUE, -276),
0F);
+        Assert.assertEquals(Float.MIN_VALUE,          FastMath.scalb(Float.MAX_VALUE, -277),
0F);
+        Assert.assertEquals(0,                        FastMath.scalb(Float.MAX_VALUE, -278),
0F);
+        Assert.assertEquals(Float.POSITIVE_INFINITY,  FastMath.scalb(Float.POSITIVE_INFINITY,
-1000000), 0F);
+        Assert.assertEquals(-3.13994498e38f,          FastMath.scalb(-1.1e-7f,         151),
0F);
+        Assert.assertEquals(Float.NEGATIVE_INFINITY,  FastMath.scalb(-1.1e-7f,         152),
0F);
+    }
+
     private static void reportError(String message) {
         final boolean fatal = false;
         if (fatal) {

Modified: commons/proper/math/branches/MATH_2_X/src/test/java/org/apache/commons/math/util/MathUtilsTest.java
URL: http://svn.apache.org/viewvc/commons/proper/math/branches/MATH_2_X/src/test/java/org/apache/commons/math/util/MathUtilsTest.java?rev=1062549&r1=1062548&r2=1062549&view=diff
==============================================================================
--- commons/proper/math/branches/MATH_2_X/src/test/java/org/apache/commons/math/util/MathUtilsTest.java
(original)
+++ commons/proper/math/branches/MATH_2_X/src/test/java/org/apache/commons/math/util/MathUtilsTest.java
Sun Jan 23 22:19:37 2011
@@ -957,16 +957,6 @@ public final class MathUtilsTest extends
         }
     }
 
-    public void testScalb() {
-        assertEquals( 0.0, MathUtils.scalb(0.0, 5), 1.0e-15);
-        assertEquals(32.0, MathUtils.scalb(1.0, 5), 1.0e-15);
-        assertEquals(1.0 / 32.0, MathUtils.scalb(1.0,  -5), 1.0e-15);
-        assertEquals(FastMath.PI, MathUtils.scalb(FastMath.PI, 0), 1.0e-15);
-        assertTrue(Double.isInfinite(MathUtils.scalb(Double.POSITIVE_INFINITY, 1)));
-        assertTrue(Double.isInfinite(MathUtils.scalb(Double.NEGATIVE_INFINITY, 1)));
-        assertTrue(Double.isNaN(MathUtils.scalb(Double.NaN, 1)));
-    }
-
     public void testNormalizeAngle() {
         for (double a = -15.0; a <= 15.0; a += 0.1) {
             for (double b = -15.0; b <= 15.0; b += 0.2) {



Mime
View raw message