classlib: math fixes (#742)

This commit is contained in:
Ivan Hetman 2023-09-19 10:52:10 +03:00 committed by GitHub
parent 4a081db1c3
commit 0997a65596
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 116 additions and 18 deletions

View File

@ -21,6 +21,7 @@ import org.teavm.classlib.PlatformDetector;
import org.teavm.interop.Import; import org.teavm.interop.Import;
import org.teavm.interop.NoSideEffects; import org.teavm.interop.NoSideEffects;
import org.teavm.interop.Unmanaged; import org.teavm.interop.Unmanaged;
import org.teavm.jso.JSBody;
@NoSideEffects @NoSideEffects
public final class TMath extends TObject { public final class TMath extends TObject {
@ -207,19 +208,33 @@ public final class TMath extends TObject {
} }
public static int abs(int n) { public static int abs(int n) {
return n > 0 ? n : -n; return n >= 0 ? n : -n;
} }
public static long abs(long n) { public static long abs(long n) {
return n > 0 ? n : -n; return n >= 0 ? n : -n;
} }
@JSBody(params = "d", script = "return Math.abs(d);")
@NoSideEffects
private static native float jsAbs(float d);
public static float abs(float n) { public static float abs(float n) {
return n > 0 ? n : -n; if (PlatformDetector.isJavaScript()) {
return jsAbs(n);
}
return n <= 0.0f ? 0.0f - n : n;
} }
@JSBody(params = "d", script = "return Math.abs(d);")
@NoSideEffects
private static native double jsAbs(double d);
public static double abs(double n) { public static double abs(double n) {
return n > 0 ? n : -n; if (PlatformDetector.isJavaScript()) {
return jsAbs(n);
}
return n <= 0.0d ? 0.0d - n : n;
} }
public static double ulp(double d) { public static double ulp(double d) {
@ -241,7 +256,7 @@ public final class TMath extends TObject {
bits -= 52L << 52L; bits -= 52L << 52L;
} else { } else {
int exponent = (int) (bits >> 52); int exponent = (int) (bits >> 52);
bits = 1 << Math.max(0, exponent - 1); bits = 1L << Math.max(0, exponent - 1);
} }
return TDouble.longBitsToDouble(bits); return TDouble.longBitsToDouble(bits);
} }
@ -255,8 +270,8 @@ public final class TMath extends TObject {
int bits = TFloat.floatToIntBits(d); int bits = TFloat.floatToIntBits(d);
bits &= 0x7F800000; bits &= 0x7F800000;
if (bits >= 24L << 23L) { if (bits >= 24 << 23) {
bits -= 23L << 23L; bits -= 23 << 23;
} else { } else {
int exponent = bits >> 23; int exponent = bits >> 23;
bits = 1 << Math.max(0, exponent - 1); bits = 1 << Math.max(0, exponent - 1);
@ -264,12 +279,32 @@ public final class TMath extends TObject {
return TFloat.intBitsToFloat(bits); return TFloat.intBitsToFloat(bits);
} }
@JSBody(params = "d", script = "return Math.sign(d);")
@NoSideEffects
private static native double sign(double d);
public static double signum(double d) { public static double signum(double d) {
return d > 0 ? 1 : d < -0 ? -1 : d; if (PlatformDetector.isJavaScript()) {
return sign(d);
}
if (d == 0.0 || Double.isNaN(d)) {
return d;
}
return d < 0.0 ? -1.0 : 1.0;
} }
@JSBody(params = "d", script = "return Math.sign(d);")
@NoSideEffects
private static native float sign(float d);
public static float signum(float d) { public static float signum(float d) {
return d > 0 ? 1 : d < -0 ? -1 : d; if (PlatformDetector.isJavaScript()) {
return sign(d);
}
if (d == 0.0f || Float.isNaN(d)) {
return d;
}
return d < 0.0f ? -1.0f : 1.0f;
} }
public static double sinh(double x) { public static double sinh(double x) {
@ -343,12 +378,14 @@ public final class TMath extends TObject {
if (TDouble.isNaN(d)) { if (TDouble.isNaN(d)) {
return d; return d;
} }
if (d == 0.0d) {
return Double.MIN_VALUE;
}
if (d == TDouble.POSITIVE_INFINITY) { if (d == TDouble.POSITIVE_INFINITY) {
return d; return d;
} }
long bits = TDouble.doubleToLongBits(d); long bits = TDouble.doubleToLongBits(d);
boolean negative = (bits & (1L << 63)) != 0; if (d < 0.0d) {
if (negative) {
bits--; bits--;
} else { } else {
bits++; bits++;
@ -360,12 +397,14 @@ public final class TMath extends TObject {
if (TFloat.isNaN(d)) { if (TFloat.isNaN(d)) {
return d; return d;
} }
if (d == 0.0f) {
return Float.MIN_VALUE;
}
if (d == TFloat.POSITIVE_INFINITY) { if (d == TFloat.POSITIVE_INFINITY) {
return d; return d;
} }
int bits = TFloat.floatToIntBits(d); int bits = TFloat.floatToIntBits(d);
boolean negative = (bits & (1L << 31)) != 0; if (d < 0.0f) {
if (negative) {
bits--; bits--;
} else { } else {
bits++; bits++;
@ -377,12 +416,14 @@ public final class TMath extends TObject {
if (TDouble.isNaN(d)) { if (TDouble.isNaN(d)) {
return d; return d;
} }
if (d == 0.0d) {
return -Double.MIN_VALUE;
}
if (d == TDouble.NEGATIVE_INFINITY) { if (d == TDouble.NEGATIVE_INFINITY) {
return d; return d;
} }
long bits = TDouble.doubleToLongBits(d); long bits = TDouble.doubleToLongBits(d);
boolean negative = (bits & (1L << 63)) != 0; if (d < 0.0d) {
if (negative) {
bits++; bits++;
} else { } else {
bits--; bits--;
@ -394,12 +435,14 @@ public final class TMath extends TObject {
if (TFloat.isNaN(d)) { if (TFloat.isNaN(d)) {
return d; return d;
} }
if (d == TFloat.POSITIVE_INFINITY) { if (d == 0.0f) {
return -Float.MIN_VALUE;
}
if (d == TFloat.NEGATIVE_INFINITY) {
return d; return d;
} }
int bits = TFloat.floatToIntBits(d); int bits = TFloat.floatToIntBits(d);
boolean negative = (bits & (1L << 31)) != 0; if (d < 0.0f) {
if (negative) {
bits++; bits++;
} else { } else {
bits--; bits--;

View File

@ -16,6 +16,7 @@
package org.teavm.classlib.java.lang; package org.teavm.classlib.java.lang;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.teavm.junit.TeaVMTestRunner; import org.teavm.junit.TeaVMTestRunner;
@ -59,6 +60,32 @@ public class MathTest {
assertEquals(6, Math.getExponent(123.456)); assertEquals(6, Math.getExponent(123.456));
} }
@Test
public void testAbs() {
assertEquals(Float.valueOf(Float.POSITIVE_INFINITY), Float.valueOf(Math.abs(Float.NEGATIVE_INFINITY)));
assertEquals(Double.valueOf(Double.POSITIVE_INFINITY), Double.valueOf(Math.abs(Double.NEGATIVE_INFINITY)));
assertEquals(Double.valueOf(5.0), Double.valueOf(Math.abs(-5.0)));
assertEquals(Double.valueOf(3.0), Double.valueOf(Math.abs(3.0)));
assertEquals(Double.valueOf(3.0), Double.valueOf(Math.abs(-3.0)));
assertEquals(Double.valueOf(5.0), Double.valueOf(Math.abs(5.0)));
assertEquals(Float.valueOf(0.0f), Float.valueOf(Math.abs(-0.0f)));
assertEquals(Float.valueOf(0.0f), Float.valueOf(Math.abs(0.0f)));
assertEquals(Double.valueOf(0.0), Double.valueOf(Math.abs(-0.0)));
assertEquals(Double.valueOf(0.0), Double.valueOf(Math.abs(0.0)));
}
@Test
public void signumWorks() {
assertEquals(Double.valueOf(0.0), Double.valueOf(Math.signum(0.0)));
assertEquals(Double.valueOf(-0.0), Double.valueOf(Math.signum(-0.0)));
assertTrue(Double.isNaN(Math.signum(Double.NaN)));
assertEquals(Float.valueOf(0.0f), Float.valueOf(Math.signum(0.0f)));
assertEquals(Float.valueOf(-0.0f), Float.valueOf(Math.signum(-0.0f)));
assertTrue(Float.isNaN(Math.signum(Float.NaN)));
assertEquals(Double.valueOf(-1.0), Double.valueOf(Math.signum(-Double.MIN_VALUE)));
assertEquals(Double.valueOf(1.0), Double.valueOf(Math.signum(Double.MIN_VALUE)));
}
@Test @Test
public void roundWorks() { public void roundWorks() {
assertEquals(1, Math.round(1.3)); assertEquals(1, Math.round(1.3));
@ -66,4 +93,32 @@ public class MathTest {
assertEquals(-1, Math.round(-1.3)); assertEquals(-1, Math.round(-1.3));
assertEquals(-2, Math.round(-1.8)); assertEquals(-2, Math.round(-1.8));
} }
@Test
public void nextWorks() {
assertEquals(Double.valueOf(-Double.MIN_VALUE), Double.valueOf(Math.nextDown(0.0)));
assertEquals(Double.valueOf(Double.MIN_VALUE), Double.valueOf(Math.nextUp(0.0)));
assertEquals(Float.valueOf(-Float.MIN_VALUE), Float.valueOf(Math.nextDown(0.0f)));
assertEquals(Float.valueOf(Float.MIN_VALUE), Float.valueOf(Math.nextUp(0.0f)));
assertEquals(Double.valueOf(0.10000000000000002), Double.valueOf(Math.nextUp(0.1)));
assertEquals(Double.valueOf(0.9999999999999999), Double.valueOf(Math.nextDown(1.0)));
assertEquals(Double.valueOf(-0.09999999999999999), Double.valueOf(Math.nextUp(-0.1)));
assertEquals(Double.valueOf(-1.0000000000000002), Double.valueOf(Math.nextDown(-1.0)));
assertEquals(Float.valueOf(0.10000001f), Float.valueOf(Math.nextUp(0.1f)));
assertEquals(Float.valueOf(0.99999994f), Float.valueOf(Math.nextDown(1.0f)));
assertEquals(Float.valueOf(-0.099999994f), Float.valueOf(Math.nextUp(-0.1f)));
assertEquals(Float.valueOf(-1.0000001f), Float.valueOf(Math.nextDown(-1.0f)));
assertEquals(Float.valueOf(Float.NEGATIVE_INFINITY), Float.valueOf(Math.nextDown(Float.NEGATIVE_INFINITY)));
assertEquals(Float.valueOf(Float.intBitsToFloat(Float.floatToIntBits(Float.POSITIVE_INFINITY) - 1)),
Float.valueOf(Math.nextDown(Float.POSITIVE_INFINITY)));
assertEquals(Float.valueOf(Float.POSITIVE_INFINITY), Float.valueOf(Math.nextUp(Float.POSITIVE_INFINITY)));
assertEquals(Float.valueOf(Float.intBitsToFloat(Float.floatToIntBits(Float.NEGATIVE_INFINITY) - 1)),
Float.valueOf(Math.nextUp(Float.NEGATIVE_INFINITY)));
assertEquals(Double.valueOf(Double.NEGATIVE_INFINITY), Double.valueOf(Math.nextDown(Double.NEGATIVE_INFINITY)));
assertEquals(Double.valueOf(Double.longBitsToDouble(Double.doubleToLongBits(Double.POSITIVE_INFINITY) - 1)),
Double.valueOf(Math.nextDown(Double.POSITIVE_INFINITY)));
assertEquals(Double.valueOf(Double.POSITIVE_INFINITY), Double.valueOf(Math.nextUp(Double.POSITIVE_INFINITY)));
assertEquals(Double.valueOf(Double.longBitsToDouble(Double.doubleToLongBits(Double.NEGATIVE_INFINITY) - 1)),
Double.valueOf(Math.nextUp(Double.NEGATIVE_INFINITY)));
}
} }