added FastMath.scalb(double, int) and FastMath.scalb(float, int)

deprecated MathUtils.scalb(double, int)
JIRA: MATH-498

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/branches/MATH_2_X@1062549 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Luc Maisonobe 2011-01-23 22:19:37 +00:00
parent c111a2fe34
commit 9af0c6fc0f
5 changed files with 200 additions and 28 deletions

View File

@ -3400,6 +3400,162 @@ public class FastMath {
return abs(x - Float.intBitsToFloat(Float.floatToIntBits(x) ^ 1));
}
/**
* 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.

View File

@ -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);
}
/**

View File

@ -52,15 +52,19 @@ The <action> type attribute can be add,update,fix,remove.
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

View File

@ -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) {

View File

@ -957,16 +957,6 @@ public final class MathUtilsTest extends TestCase {
}
}
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) {