Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement java.lang.Math.nextAfter, nextUp, nextDown #249

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 69 additions & 8 deletions jre/java/java/lang/Math.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,6 @@ public final class Math {
// public static int getExponent (double d)
// public static int getExponent (float f)
// public static double IEEEremainder(double f1, double f2)
// public static double nextAfter(double start, double direction)
// public static float nextAfter(float start, float direction)
// public static double nextUp(double start) {
// return nextAfter(start, 1.0d);
// }
// public static float nextUp(float start) {
// return nextAfter(start,1.0f);
// }

public static final double E = 2.7182818284590452354;
public static final double PI = 3.14159265358979323846;
Expand Down Expand Up @@ -323,6 +315,75 @@ public static double toRadians(double x) {
return x * PI_OVER_180;
}

public static double nextAfter(double start, double direction) {
// Simple case described by Javadoc:
if (start == direction) {
return direction;
}

// NaN special case, if either is NaN, return NaN.
if (Double.isNaN(start) || Double.isNaN(direction)) {
return Double.NaN;
}

// The javadoc 'special cases' for infinities and min_value are handled already by manipulating
// the bits of the start value below. However, that approach used below doesn't work around
// zeros - we have two zero values to deal with (positive and negative) with very different bit
// representations (zero and Long.MIN_VALUE respectively).
if (start == 0) {
return direction > start ? Double.MIN_VALUE : -Double.MIN_VALUE;
}

// Convert to int bits and increment or decrement - the fact that two positive ieee754 float
// values can be compared as ints (or two negative values, with the comparison inverted) means
// that this trick works as naturally as A + 1 > A. NaNs and zeros were already handled above.
long bits = Double.doubleToLongBits(start);
bits += (direction > start) == (bits >= 0) ? 1 : -1;
return Double.longBitsToDouble(bits);
}

public static float nextAfter(float start, double direction) {
// Simple case described by Javadoc:
if (start == direction) {
return (float) direction;
}

// NaN special case, if either is NaN, return NaN.
if (Float.isNaN(start) || Double.isNaN(direction)) {
return Float.NaN;
}
// The javadoc 'special cases' for INFINITYs, MIN_VALUE, and MAX_VALUE are handled already by
// manipulating the bits of the start value below. However, that approach used below doesn't
// work around zeros - we have two zero values to deal with (positive and negative) with very
// different bit representations (zero and Integer.MIN_VALUE respectively).
if (start == 0) {
return direction > start ? Float.MIN_VALUE : -Float.MIN_VALUE;
}

// Convert to int bits and increment or decrement - the fact that two positive ieee754 float
// values can be compared as ints (or two negative values, with the comparison inverted) means
// that this trick works as naturally as A + 1 > A. NaNs and zeros were already handled above.
int bits = Float.floatToIntBits(start);
bits += (direction > start) == (bits >= 0) ? 1 : -1;
return Float.intBitsToFloat(bits);
}

public static double nextUp(double start) {
return nextAfter(start, Double.POSITIVE_INFINITY);
}

public static float nextUp(float start) {
return nextAfter(start, Double.POSITIVE_INFINITY);
}

public static double nextDown(double start) {
return nextAfter(start, Double.NEGATIVE_INFINITY);
}

public static float nextDown(float start) {
return nextAfter(start, Double.NEGATIVE_INFINITY);
}

private static boolean isSafeIntegerRange(double value) {
return Integer.MIN_VALUE <= value && value <= Integer.MAX_VALUE;
}
Expand Down
235 changes: 235 additions & 0 deletions jre/javatests/com/google/j2cl/jre/java/lang/MathTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -938,4 +938,239 @@ public void testScalb() {
assertEquals(4294967296.0f, Math.scalb(1f, 32));
assertEquals(2.3283064e-10f, Math.scalb(1f, -32), 1e-7f);
}

public void testNextAfterFloat() {
// Test the five "special cases" described by the Javadoc, with both Float and Double
// "direction" values.
assertNaN(Math.nextAfter(Float.NaN, Float.NaN));
assertNaN(Math.nextAfter(Float.NaN, Double.NaN));
assertNaN(Math.nextAfter(Float.NaN, 0));
assertNaN(Math.nextAfter(0, Float.NaN));
assertNaN(Math.nextAfter(0, Double.NaN));

assertNegativeZero(Math.nextAfter(0.0f, -0.0f));
assertNegativeZero(Math.nextAfter(0.0f, -0.0d));
assertNegativeZero(Math.nextAfter(-0.0f, -0.0f));
assertNegativeZero(Math.nextAfter(-0.0f, -0.0d));
assertPositiveZero(Math.nextAfter(0.0f, 0.0f));
assertPositiveZero(Math.nextAfter(0.0f, 0.0d));
assertPositiveZero(Math.nextAfter(-0.0f, 0.0f));
assertPositiveZero(Math.nextAfter(-0.0f, 0.0d));

assertNegativeZero(Math.nextAfter(-Float.MIN_VALUE, 1));
assertPositiveZero(Math.nextAfter(Float.MIN_VALUE, -1));

assertEquals(Float.MAX_VALUE, Math.nextAfter(Float.POSITIVE_INFINITY, -1));
assertEquals(Float.MAX_VALUE,
Math.nextAfter(Float.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY));
assertEquals(Float.MAX_VALUE,
Math.nextAfter(Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY));
assertEquals(-Float.MAX_VALUE,
Math.nextAfter(Float.NEGATIVE_INFINITY, 1));
assertEquals(-Float.MAX_VALUE,
Math.nextAfter(Float.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY));
assertEquals(-Float.MAX_VALUE,
Math.nextAfter(Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY));

assertEquals(Float.POSITIVE_INFINITY,
Math.nextAfter(Float.MAX_VALUE, Float.POSITIVE_INFINITY));
assertEquals(Float.POSITIVE_INFINITY,
Math.nextAfter(Float.MAX_VALUE, Double.POSITIVE_INFINITY));
assertEquals(Float.NEGATIVE_INFINITY,
Math.nextAfter(-Float.MAX_VALUE, Float.NEGATIVE_INFINITY));
assertEquals(Float.NEGATIVE_INFINITY,
Math.nextAfter(-Float.MAX_VALUE, Double.NEGATIVE_INFINITY));

// General rules: if values compare as equal, return "direction" (exceptions covered above)
assertEquals(Float.POSITIVE_INFINITY,
Math.nextAfter(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY));
assertEquals(Float.POSITIVE_INFINITY,
Math.nextAfter(Float.POSITIVE_INFINITY, Double.POSITIVE_INFINITY));
assertEquals(Float.NEGATIVE_INFINITY,
Math.nextAfter(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY));
assertEquals(Float.NEGATIVE_INFINITY,
Math.nextAfter(Float.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY));
assertEquals(Float.MAX_VALUE, Math.nextAfter(Float.MAX_VALUE, Float.MAX_VALUE));

// Return number adjacent to "start" in the relative direction of "direction". Using hex to
// easily see bit patterns in the sample data.
assertEquals(0x1.fffffcp127f, Math.nextAfter(Float.MAX_VALUE, 0));
assertEquals(0x1.fffffcp127f,
Math.nextAfter(Float.MAX_VALUE, Float.NEGATIVE_INFINITY));
assertEquals(-0x1.fffffcp127f, Math.nextAfter(-Float.MAX_VALUE, 0));
assertEquals(-0x1.fffffcp127f,
Math.nextAfter(-Float.MAX_VALUE, Float.POSITIVE_INFINITY));
assertEquals(0x1.fffffep124f, Math.nextAfter(0x1.0p125f, 0));
assertEquals(0x1.0p125f,
Math.nextAfter(0x1.fffffep124f, Float.POSITIVE_INFINITY));

// Test near zero (minvalue -> zero is tested above), mantissa sign flips positive/negative
assertEquals(Float.MIN_VALUE, Math.nextAfter(0.0f, 1));
assertEquals(Float.MIN_VALUE, Math.nextAfter(-0.0f, 1));
assertEquals(-Float.MIN_VALUE, Math.nextAfter(0.0f, -1));
assertEquals(-Float.MIN_VALUE, Math.nextAfter(-0.0f, -1));

// Test near 1, where exponent sign flips positive/negative
assertEquals(0x1.000002p0f, Math.nextAfter(1.0f, 2));
assertEquals(0x1.fffffep-1f, Math.nextAfter(1.0f, 0));
assertEquals(1.0f, Math.nextAfter(0x1.fffffep-1f, 2));
assertEquals(1.0f, Math.nextAfter(0x1.000002p0f, 0));

// Repeat near -1
assertEquals(-0x1.000002p0f, Math.nextAfter(-1.0f, -2));
assertEquals(-0x1.fffffep-1f, Math.nextAfter(-1.0f, 0));
assertEquals(-1.0f, Math.nextAfter(-0x1.fffffep-1f, -2));
assertEquals(-1.0f, Math.nextAfter(-0x1.000002p0f, 0));
}

public void testNextUpFloat() {
// Special cases from javadoc
assertNaN(Math.nextUp(Float.NaN));
assertEquals(Float.POSITIVE_INFINITY, Math.nextUp(Float.POSITIVE_INFINITY));
assertEquals(Float.MIN_VALUE, Math.nextUp(0.0f));
assertEquals(Float.MIN_VALUE, Math.nextUp(-0.0f));

assertEquals(Float.POSITIVE_INFINITY, Math.nextUp(Float.MAX_VALUE));
assertEquals(-Float.MAX_VALUE, Math.nextUp(Float.NEGATIVE_INFINITY));

assertNegativeZero(Math.nextUp(-Float.MIN_VALUE));

assertEquals(0x1.0p2f, Math.nextUp(0x1.fffffep1f));
assertEquals(0x1.000002p2f, Math.nextUp(0x1.0p2f));
}

public void testNextDownFloat() {
// Special cases from javadoc
assertNaN(Math.nextDown(Float.NaN));
assertEquals(Float.NEGATIVE_INFINITY, Math.nextDown(Float.NEGATIVE_INFINITY));
assertEquals(-Float.MIN_VALUE, Math.nextDown(0.0f));
assertEquals(-Float.MIN_VALUE, Math.nextDown(-0.0f));

assertEquals(Float.NEGATIVE_INFINITY, Math.nextDown(-Float.MAX_VALUE));
assertEquals(Float.MAX_VALUE, Math.nextDown(Float.POSITIVE_INFINITY));

assertPositiveZero(Math.nextDown(Float.MIN_VALUE));

assertEquals(0x1.fffffep1f, Math.nextDown(0x1.0p2f));
assertEquals(0x1.fffffcp1f, Math.nextDown(0x1.fffffep1f));
}

public void testNextAfterDouble() {
// Test the five "special cases" described by the Javadoc
assertNaN(Math.nextAfter(Double.NaN, Double.NaN));
assertNaN(Math.nextAfter(Double.NaN, 0));
assertNaN(Math.nextAfter(0d, Double.NaN));

assertNegativeZero(Math.nextAfter(0.0d, -0.0d));
assertNegativeZero(Math.nextAfter(-0.0d, -0.0d));
assertPositiveZero(Math.nextAfter(0.0d, 0.0d));
assertPositiveZero(Math.nextAfter(-0.0d, 0.0d));

assertNegativeZero(Math.nextAfter(-Double.MIN_VALUE, 1));
assertPositiveZero(Math.nextAfter(Double.MIN_VALUE, -1));

assertEquals(Double.MAX_VALUE, Math.nextAfter(Double.POSITIVE_INFINITY, -1));
assertEquals(Double.MAX_VALUE,
Math.nextAfter(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY));
assertEquals(-Double.MAX_VALUE, Math.nextAfter(Double.NEGATIVE_INFINITY, 1));
assertEquals(-Double.MAX_VALUE,
Math.nextAfter(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY));

assertEquals(Double.POSITIVE_INFINITY,
Math.nextAfter(Double.MAX_VALUE, Double.POSITIVE_INFINITY));
assertEquals(Double.NEGATIVE_INFINITY,
Math.nextAfter(-Double.MAX_VALUE, Double.NEGATIVE_INFINITY));

// General rules: if values compare as equal, return "direction" (exceptions covered above)
assertEquals(Double.POSITIVE_INFINITY,
Math.nextAfter(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY));
assertEquals(Double.NEGATIVE_INFINITY,
Math.nextAfter(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY));
assertEquals(Double.MAX_VALUE, Math.nextAfter(Double.MAX_VALUE, Double.MAX_VALUE));

// Return number adjacent to "start" in the relative direction of "direction". Using hex to
// easily see bit patterns in the sample data.
assertEquals(0x1.ffffffffffffep1023, Math.nextAfter(Double.MAX_VALUE, 0));
assertEquals(0x1.ffffffffffffep1023,
Math.nextAfter(Double.MAX_VALUE, Double.NEGATIVE_INFINITY));
assertEquals(-0x1.ffffffffffffep1023, Math.nextAfter(-Double.MAX_VALUE, 0));
assertEquals(-0x1.ffffffffffffep1023,
Math.nextAfter(-Double.MAX_VALUE, Double.POSITIVE_INFINITY));
assertEquals(0x1.fffffffffffffp124, Math.nextAfter(0x1.0p125, 0));
assertEquals(0x1.0p125, Math.nextAfter(0x1.fffffffffffffp124, Double.POSITIVE_INFINITY));

// Test near zero (minvalue -> zero is tested above), mantissa sign flips positive/negative
assertEquals(Double.MIN_VALUE, Math.nextAfter(0.0d, 1));
assertEquals(Double.MIN_VALUE, Math.nextAfter(-0.0d, 1));
assertEquals(-Double.MIN_VALUE, Math.nextAfter(0.0d, -1));
assertEquals(-Double.MIN_VALUE, Math.nextAfter(-0.0d, -1));

// Test near 1, where exponent sign flips positive/negative
assertEquals(0x1.0000000000001p0d, Math.nextAfter(1.0d, 2));
assertEquals(0x1.fffffffffffffp-1d, Math.nextAfter(1.0d, 0));
assertEquals(1.0d, Math.nextAfter(0x1.fffffffffffffp-1d, 2));
assertEquals(1.0d, Math.nextAfter(0x1.0000000000001p0d, 0));

// Repeat near -1
assertEquals(-0x1.0000000000001p0d, Math.nextAfter(-1.0d, -2));
assertEquals(-0x1.fffffffffffffp-1d, Math.nextAfter(-1.0d, 0));
assertEquals(-1.0d, Math.nextAfter(-0x1.fffffffffffffp-1d, -2));
assertEquals(-1.0d, Math.nextAfter(-0x1.0000000000001p0d, 0));
}

public void testNextUpDouble() {
// Special cases from javadoc
assertNaN(Math.nextUp(Double.NaN));
assertEquals(Double.POSITIVE_INFINITY, Math.nextUp(Double.POSITIVE_INFINITY));
assertEquals(Double.MIN_VALUE, Math.nextUp(0.0));
assertEquals(Double.MIN_VALUE, Math.nextUp(-0.0));

assertEquals(Double.POSITIVE_INFINITY, Math.nextUp(Double.MAX_VALUE));
assertEquals(-Double.MAX_VALUE, Math.nextUp(Double.NEGATIVE_INFINITY));

assertNegativeZero(Math.nextUp(-Double.MIN_VALUE));

assertEquals(0x1.0p2d, Math.nextUp(0x1.fffffffffffffp1d));
assertEquals(0x1.0000000000001p2d, Math.nextUp(0x1.0p2d));

// Test near zero (minvalue -> zero is tested above), mantissa sign flips positive/negative
assertEquals(Double.MIN_VALUE, Math.nextUp(0.0d));
assertEquals(Double.MIN_VALUE, Math.nextUp(-0.0d));

// Test near 1, where exponent sign flips positive/negative
assertEquals(0x1.0000000000001p0d, Math.nextUp(1.0d));
assertEquals(1.0d, Math.nextUp(0x1.fffffffffffffp-1d));

// Repeat near -1
assertEquals(-0x1.fffffffffffffp-1d, Math.nextUp(-1.0d));
assertEquals(-1.0d, Math.nextUp(-0x1.0000000000001p0d));
}

public void testNextDownDouble() {
// Special cases from javadoc
assertNaN(Math.nextDown(Double.NaN));
assertEquals(Double.NEGATIVE_INFINITY, Math.nextDown(Double.NEGATIVE_INFINITY));
assertEquals(-Double.MIN_VALUE, Math.nextDown(0.0d));
assertEquals(-Double.MIN_VALUE, Math.nextDown(-0.0d));

assertEquals(Double.NEGATIVE_INFINITY, Math.nextDown(-Double.MAX_VALUE));
assertEquals(Double.MAX_VALUE, Math.nextDown(Double.POSITIVE_INFINITY));

assertPositiveZero(Math.nextDown(Double.MIN_VALUE));

assertEquals(0x1.fffffffffffffp1d, Math.nextDown(0x1.0p2d));
assertEquals(0x1.ffffffffffffep1d, Math.nextDown(0x1.fffffffffffffp1d));

// Test near zero (minvalue -> zero is tested above), mantissa sign flips positive/negative
assertEquals(-Double.MIN_VALUE, Math.nextDown(0.0d));
assertEquals(-Double.MIN_VALUE, Math.nextDown(-0.0d));

// Test near 1, where exponent sign flips positive/negative
assertEquals(0x1.fffffffffffffp-1d, Math.nextDown(1.0d));
assertEquals(1.0d, Math.nextDown(0x1.0000000000001p0d));

// Repeat near -1
assertEquals(-0x1.0000000000001p0d, Math.nextDown(-1.0d));
assertEquals(-1.0d, Math.nextDown(-0x1.fffffffffffffp-1d));
}
}