fixed nextAfter implementations for handling of some special values

fixed the signature of the float version, as the spec is to have a double second argument
moved the existing tests that were used in the former implementation in MathUtils,
fixing them also as two of them were not compliant with the spec for equal numbers
Jira: MATH-478

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/trunk@1062387 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Luc Maisonobe 2011-01-23 11:06:24 +00:00
parent f806576392
commit 4e017170f5
4 changed files with 119 additions and 151 deletions

View File

@ -24,7 +24,6 @@ package org.apache.commons.math.util;
* <li>{@link #asinh(double)}</li>
* <li>{@link #acosh(double)}</li>
* <li>{@link #atanh(double)}</li>
* <li>{@link #nextAfter(float,float)}</li>
* </ul>
* The following methods are found in StrictMath since 1.6 only
* <ul>
@ -34,6 +33,7 @@ package org.apache.commons.math.util;
* <li>{@link #nextUp(double)}</li>
* <li>{@link #copySign(float, float)}</li>
* <li>{@link #getExponent(float)}</li>
* <li>{@link #nextAfter(float,double)}</li>
* <li>{@link #nextUp(float)}</li>
* </ul>
* @version $Revision$ $Date$
@ -3420,47 +3420,24 @@ public class FastMath {
public static double nextAfter(double d, double direction) {
// handling of some important special cases
if (Double.isNaN(d)) {
return d;
if (Double.isNaN(d) || Double.isNaN(direction)) {
return Double.NaN;
} else if (d == direction) {
return direction;
} else if (Double.isInfinite(d)) {
if (d < direction) {
return -Double.MAX_VALUE;
} else if (direction < d) {
return Double.MAX_VALUE;
} else {
return d;
}
return (d < 0) ? -Double.MAX_VALUE : Double.MAX_VALUE;
} else if (d == 0) {
return (direction < 0) ? -Double.MIN_VALUE : Double.MIN_VALUE;
}
// special cases MAX_VALUE to infinity and MIN_VALUE to 0
// are handled just as normal numbers
// split the double in raw components
long bits = Double.doubleToLongBits(d);
long sign = bits & 0x8000000000000000L;
long exponent = bits & 0x7ff0000000000000L;
long mantissa = bits & 0x000fffffffffffffL;
if (d * (direction - d) >= 0) {
// we should increase the mantissa
if (mantissa == 0x000fffffffffffffL) {
return Double.longBitsToDouble(sign |
(exponent + 0x0010000000000000L));
} else {
return Double.longBitsToDouble(sign |
exponent | (mantissa + 1));
}
final long bits = Double.doubleToLongBits(d);
final long sign = bits & 0x8000000000000000L;
if ((direction < d) ^ (sign == 0L)) {
return Double.longBitsToDouble(sign | ((bits & 0x7fffffffffffffffL) + 1));
} else {
// we should decrease the mantissa
if (mantissa == 0L) {
return Double.longBitsToDouble(sign |
(exponent - 0x0010000000000000L) |
0x000fffffffffffffL);
} else {
return Double.longBitsToDouble(sign |
exponent | (mantissa - 1));
}
return Double.longBitsToDouble(sign | ((bits & 0x7fffffffffffffffL) - 1));
}
}
@ -3482,45 +3459,27 @@ public class FastMath {
* direction is greater or smaller than f)
* @return the next machine representable number in the specified direction
*/
public static float nextAfter(float f, float direction) {
public static float nextAfter(final float f, final double direction) {
// handling of some important special cases
if (Float.isNaN(f)) {
return f;
if (Double.isNaN(f) || Double.isNaN(direction)) {
return Float.NaN;
} else if (f == direction) {
return (float) direction;
} else if (Float.isInfinite(f)) {
if (f < direction) {
return -Float.MAX_VALUE;
} else if (direction < f) {
return Float.MAX_VALUE;
} else {
return f;
}
return (f < 0f) ? -Float.MAX_VALUE : Float.MAX_VALUE;
} else if (f == 0f) {
return (direction < 0f) ? -Float.MIN_VALUE : Float.MIN_VALUE;
return (direction < 0) ? -Float.MIN_VALUE : Float.MIN_VALUE;
}
// special cases MAX_VALUE to infinity and MIN_VALUE to 0
// are handled just as normal numbers
// split the double in raw components
int bits = Float.floatToIntBits(f);
int sign = bits & 0x80000000;
int exponent = bits & 0x7f800000;
int mantissa = bits & 0x007fffff;
if (f * (direction - f) >= 0f) {
// we should increase the mantissa
if (mantissa == 0x000fffff) {
return Float.intBitsToFloat(sign | (exponent + 0x00800000));
} else {
return Float.intBitsToFloat(sign | exponent | (mantissa + 1));
}
final int bits = Float.floatToIntBits(f);
final int sign = bits & 0x80000000;
if ((direction < f) ^ (sign == 0)) {
return Float.intBitsToFloat(sign | ((bits & 0x7fffffff) + 1));
} else {
// we should decrease the mantissa
if (mantissa == 0) {
return Float.intBitsToFloat(sign | (exponent - 0x00800000) | 0x007fffff);
} else {
return Float.intBitsToFloat(sign | exponent | (mantissa - 1));
}
return Float.intBitsToFloat(sign | ((bits & 0x7fffffff) - 1));
}
}

View File

@ -183,6 +183,12 @@ The <action> type attribute can be add,update,fix,remove.
</action>
</release>
<release version="2.2" date="TBD" description="TBD">
<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)
(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)

View File

@ -976,6 +976,96 @@ public class FastMathTest {
}
@Test
public void testNextAfter() {
// 0x402fffffffffffff 0x404123456789abcd -> 4030000000000000
Assert.assertEquals(16.0, FastMath.nextAfter(15.999999999999998, 34.27555555555555), 0.0);
// 0xc02fffffffffffff 0x404123456789abcd -> c02ffffffffffffe
Assert.assertEquals(-15.999999999999996, FastMath.nextAfter(-15.999999999999998, 34.27555555555555), 0.0);
// 0x402fffffffffffff 0x400123456789abcd -> 402ffffffffffffe
Assert.assertEquals(15.999999999999996, FastMath.nextAfter(15.999999999999998, 2.142222222222222), 0.0);
// 0xc02fffffffffffff 0x400123456789abcd -> c02ffffffffffffe
Assert.assertEquals(-15.999999999999996, FastMath.nextAfter(-15.999999999999998, 2.142222222222222), 0.0);
// 0x4020000000000000 0x404123456789abcd -> 4020000000000001
Assert.assertEquals(8.000000000000002, FastMath.nextAfter(8.0, 34.27555555555555), 0.0);
// 0xc020000000000000 0x404123456789abcd -> c01fffffffffffff
Assert.assertEquals(-7.999999999999999, FastMath.nextAfter(-8.0, 34.27555555555555), 0.0);
// 0x4020000000000000 0x400123456789abcd -> 401fffffffffffff
Assert.assertEquals(7.999999999999999, FastMath.nextAfter(8.0, 2.142222222222222), 0.0);
// 0xc020000000000000 0x400123456789abcd -> c01fffffffffffff
Assert.assertEquals(-7.999999999999999, FastMath.nextAfter(-8.0, 2.142222222222222), 0.0);
// 0x3f2e43753d36a223 0x3f2e43753d36a224 -> 3f2e43753d36a224
Assert.assertEquals(2.308922399667661E-4, FastMath.nextAfter(2.3089223996676606E-4, 2.308922399667661E-4), 0.0);
// 0x3f2e43753d36a223 0x3f2e43753d36a223 -> 3f2e43753d36a223
Assert.assertEquals(2.3089223996676606E-4, FastMath.nextAfter(2.3089223996676606E-4, 2.3089223996676606E-4), 0.0);
// 0x3f2e43753d36a223 0x3f2e43753d36a222 -> 3f2e43753d36a222
Assert.assertEquals(2.3089223996676603E-4, FastMath.nextAfter(2.3089223996676606E-4, 2.3089223996676603E-4), 0.0);
// 0x3f2e43753d36a223 0xbf2e43753d36a224 -> 3f2e43753d36a222
Assert.assertEquals(2.3089223996676603E-4, FastMath.nextAfter(2.3089223996676606E-4, -2.308922399667661E-4), 0.0);
// 0x3f2e43753d36a223 0xbf2e43753d36a223 -> 3f2e43753d36a222
Assert.assertEquals(2.3089223996676603E-4, FastMath.nextAfter(2.3089223996676606E-4, -2.3089223996676606E-4), 0.0);
// 0x3f2e43753d36a223 0xbf2e43753d36a222 -> 3f2e43753d36a222
Assert.assertEquals(2.3089223996676603E-4, FastMath.nextAfter(2.3089223996676606E-4, -2.3089223996676603E-4), 0.0);
// 0xbf2e43753d36a223 0x3f2e43753d36a224 -> bf2e43753d36a222
Assert.assertEquals(-2.3089223996676603E-4, FastMath.nextAfter(-2.3089223996676606E-4, 2.308922399667661E-4), 0.0);
// 0xbf2e43753d36a223 0x3f2e43753d36a223 -> bf2e43753d36a222
Assert.assertEquals(-2.3089223996676603E-4, FastMath.nextAfter(-2.3089223996676606E-4, 2.3089223996676606E-4), 0.0);
// 0xbf2e43753d36a223 0x3f2e43753d36a222 -> bf2e43753d36a222
Assert.assertEquals(-2.3089223996676603E-4, FastMath.nextAfter(-2.3089223996676606E-4, 2.3089223996676603E-4), 0.0);
// 0xbf2e43753d36a223 0xbf2e43753d36a224 -> bf2e43753d36a224
Assert.assertEquals(-2.308922399667661E-4, FastMath.nextAfter(-2.3089223996676606E-4, -2.308922399667661E-4), 0.0);
// 0xbf2e43753d36a223 0xbf2e43753d36a223 -> bf2e43753d36a223
Assert.assertEquals(-2.3089223996676606E-4, FastMath.nextAfter(-2.3089223996676606E-4, -2.3089223996676606E-4), 0.0);
// 0xbf2e43753d36a223 0xbf2e43753d36a222 -> bf2e43753d36a222
Assert.assertEquals(-2.3089223996676603E-4, FastMath.nextAfter(-2.3089223996676606E-4, -2.3089223996676603E-4), 0.0);
}
@Test
public void testDoubleNextAfterSpecialCases() {
Assert.assertEquals(-Double.MAX_VALUE,FastMath.nextAfter(Double.NEGATIVE_INFINITY, 0D), 0D);
Assert.assertEquals(Double.MAX_VALUE,FastMath.nextAfter(Double.POSITIVE_INFINITY, 0D), 0D);
Assert.assertEquals(Double.NaN,FastMath.nextAfter(Double.NaN, 0D), 0D);
Assert.assertEquals(Double.POSITIVE_INFINITY,FastMath.nextAfter(Double.MAX_VALUE, Double.POSITIVE_INFINITY), 0D);
Assert.assertEquals(Double.NEGATIVE_INFINITY,FastMath.nextAfter(-Double.MAX_VALUE, Double.NEGATIVE_INFINITY), 0D);
Assert.assertEquals(Double.MIN_VALUE, FastMath.nextAfter(0D, 1D), 0D);
Assert.assertEquals(-Double.MIN_VALUE, FastMath.nextAfter(0D, -1D), 0D);
Assert.assertEquals(0D, FastMath.nextAfter(Double.MIN_VALUE, -1), 0D);
Assert.assertEquals(0D, FastMath.nextAfter(-Double.MIN_VALUE, 1), 0D);
}
@Test
public void testFloatNextAfterSpecialCases() {
Assert.assertEquals(-Float.MAX_VALUE,FastMath.nextAfter(Float.NEGATIVE_INFINITY, 0F), 0F);
Assert.assertEquals(Float.MAX_VALUE,FastMath.nextAfter(Float.POSITIVE_INFINITY, 0F), 0F);
Assert.assertEquals(Float.NaN,FastMath.nextAfter(Float.NaN, 0F), 0F);
Assert.assertEquals(Float.POSITIVE_INFINITY,FastMath.nextAfter(Float.MAX_VALUE, Float.POSITIVE_INFINITY), 0F);
Assert.assertEquals(Float.NEGATIVE_INFINITY,FastMath.nextAfter(-Float.MAX_VALUE, Float.NEGATIVE_INFINITY), 0F);
Assert.assertEquals(Float.MIN_VALUE, FastMath.nextAfter(0F, 1F), 0F);
Assert.assertEquals(-Float.MIN_VALUE, FastMath.nextAfter(0F, -1F), 0F);
Assert.assertEquals(0F, FastMath.nextAfter(Float.MIN_VALUE, -1F), 0F);
Assert.assertEquals(0F, FastMath.nextAfter(-Float.MIN_VALUE, 1F), 0F);
}
private static void reportError(String message) {
final boolean fatal = false;
if (fatal) {

View File

@ -958,93 +958,6 @@ public final class MathUtilsTest extends TestCase {
}
}
public void testNextAfter() {
// 0x402fffffffffffff 0x404123456789abcd -> 4030000000000000
assertEquals(16.0, FastMath.nextAfter(15.999999999999998, 34.27555555555555), 0.0);
// 0xc02fffffffffffff 0x404123456789abcd -> c02ffffffffffffe
assertEquals(-15.999999999999996, FastMath.nextAfter(-15.999999999999998, 34.27555555555555), 0.0);
// 0x402fffffffffffff 0x400123456789abcd -> 402ffffffffffffe
assertEquals(15.999999999999996, FastMath.nextAfter(15.999999999999998, 2.142222222222222), 0.0);
// 0xc02fffffffffffff 0x400123456789abcd -> c02ffffffffffffe
assertEquals(-15.999999999999996, FastMath.nextAfter(-15.999999999999998, 2.142222222222222), 0.0);
// 0x4020000000000000 0x404123456789abcd -> 4020000000000001
assertEquals(8.000000000000002, FastMath.nextAfter(8.0, 34.27555555555555), 0.0);
// 0xc020000000000000 0x404123456789abcd -> c01fffffffffffff
assertEquals(-7.999999999999999, FastMath.nextAfter(-8.0, 34.27555555555555), 0.0);
// 0x4020000000000000 0x400123456789abcd -> 401fffffffffffff
assertEquals(7.999999999999999, FastMath.nextAfter(8.0, 2.142222222222222), 0.0);
// 0xc020000000000000 0x400123456789abcd -> c01fffffffffffff
assertEquals(-7.999999999999999, FastMath.nextAfter(-8.0, 2.142222222222222), 0.0);
// 0x3f2e43753d36a223 0x3f2e43753d36a224 -> 3f2e43753d36a224
assertEquals(2.308922399667661E-4, FastMath.nextAfter(2.3089223996676606E-4, 2.308922399667661E-4), 0.0);
// 0x3f2e43753d36a223 0x3f2e43753d36a223 -> 3f2e43753d36a224
assertEquals(2.308922399667661E-4, FastMath.nextAfter(2.3089223996676606E-4, 2.3089223996676606E-4), 0.0);
// 0x3f2e43753d36a223 0x3f2e43753d36a222 -> 3f2e43753d36a222
assertEquals(2.3089223996676603E-4, FastMath.nextAfter(2.3089223996676606E-4, 2.3089223996676603E-4), 0.0);
// 0x3f2e43753d36a223 0xbf2e43753d36a224 -> 3f2e43753d36a222
assertEquals(2.3089223996676603E-4, FastMath.nextAfter(2.3089223996676606E-4, -2.308922399667661E-4), 0.0);
// 0x3f2e43753d36a223 0xbf2e43753d36a223 -> 3f2e43753d36a222
assertEquals(2.3089223996676603E-4, FastMath.nextAfter(2.3089223996676606E-4, -2.3089223996676606E-4), 0.0);
// 0x3f2e43753d36a223 0xbf2e43753d36a222 -> 3f2e43753d36a222
assertEquals(2.3089223996676603E-4, FastMath.nextAfter(2.3089223996676606E-4, -2.3089223996676603E-4), 0.0);
// 0xbf2e43753d36a223 0x3f2e43753d36a224 -> bf2e43753d36a222
assertEquals(-2.3089223996676603E-4, FastMath.nextAfter(-2.3089223996676606E-4, 2.308922399667661E-4), 0.0);
// 0xbf2e43753d36a223 0x3f2e43753d36a223 -> bf2e43753d36a222
assertEquals(-2.3089223996676603E-4, FastMath.nextAfter(-2.3089223996676606E-4, 2.3089223996676606E-4), 0.0);
// 0xbf2e43753d36a223 0x3f2e43753d36a222 -> bf2e43753d36a222
assertEquals(-2.3089223996676603E-4, FastMath.nextAfter(-2.3089223996676606E-4, 2.3089223996676603E-4), 0.0);
// 0xbf2e43753d36a223 0xbf2e43753d36a224 -> bf2e43753d36a224
assertEquals(-2.308922399667661E-4, FastMath.nextAfter(-2.3089223996676606E-4, -2.308922399667661E-4), 0.0);
// 0xbf2e43753d36a223 0xbf2e43753d36a223 -> bf2e43753d36a224
assertEquals(-2.308922399667661E-4, FastMath.nextAfter(-2.3089223996676606E-4, -2.3089223996676606E-4), 0.0);
// 0xbf2e43753d36a223 0xbf2e43753d36a222 -> bf2e43753d36a222
assertEquals(-2.3089223996676603E-4, FastMath.nextAfter(-2.3089223996676606E-4, -2.3089223996676603E-4), 0.0);
}
public void testDoubleNextAfterSpecialCases() {
assertEquals(-Double.MAX_VALUE,FastMath.nextAfter(Double.NEGATIVE_INFINITY, 0D));
assertEquals(Double.MAX_VALUE,FastMath.nextAfter(Double.POSITIVE_INFINITY, 0D));
assertEquals(Double.NaN,FastMath.nextAfter(Double.NaN, 0D));
assertEquals(Double.POSITIVE_INFINITY,FastMath.nextAfter(Double.MAX_VALUE, Double.POSITIVE_INFINITY));
assertEquals(Double.NEGATIVE_INFINITY,FastMath.nextAfter(-Double.MAX_VALUE, Double.NEGATIVE_INFINITY));
assertEquals(Double.MIN_VALUE, FastMath.nextAfter(0D, 1D), 0D);
assertEquals(-Double.MIN_VALUE, FastMath.nextAfter(0D, -1D), 0D);
assertEquals(0D, FastMath.nextAfter(Double.MIN_VALUE, -1), 0D);
assertEquals(0D, FastMath.nextAfter(-Double.MIN_VALUE, 1), 0D);
}
public void testFloatNextAfterSpecialCases() {
assertEquals(-Float.MAX_VALUE,FastMath.nextAfter(Float.NEGATIVE_INFINITY, 0F));
assertEquals(Float.MAX_VALUE,FastMath.nextAfter(Float.POSITIVE_INFINITY, 0F));
assertEquals(Float.NaN,FastMath.nextAfter(Float.NaN, 0F));
assertEquals(Float.POSITIVE_INFINITY,FastMath.nextAfter(Float.MAX_VALUE, Float.POSITIVE_INFINITY));
assertEquals(Float.NEGATIVE_INFINITY,FastMath.nextAfter(-Float.MAX_VALUE, Float.NEGATIVE_INFINITY));
assertEquals(Float.MIN_VALUE, FastMath.nextAfter(0F, 1F), 0F);
assertEquals(-Float.MIN_VALUE, FastMath.nextAfter(0F, -1F), 0F);
assertEquals(0F, FastMath.nextAfter(Float.MIN_VALUE, -1F), 0F);
assertEquals(0F, FastMath.nextAfter(-Float.MIN_VALUE, 1F), 0F);
}
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);