From 56ff3fbdd81318cbe5e6a25235c56dd125ac0021 Mon Sep 17 00:00:00 2001 From: ihromant Date: Mon, 25 Sep 2023 23:13:18 +0300 Subject: [PATCH] classlib: fix Math min/max functions --- .../org/teavm/classlib/java/lang/TMath.java | 60 ++++++- .../teavm/classlib/java/lang/MathTest.java | 159 +++++++++++------- 2 files changed, 150 insertions(+), 69 deletions(-) diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TMath.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TMath.java index d6b4b764e..a795ac211 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TMath.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TMath.java @@ -195,20 +195,72 @@ public final class TMath extends TObject { return a > b ? a : b; } + @GeneratedBy(MathNativeGenerator.class) + @NoSideEffects + private static native float minImpl(double a, double b); + public static double min(double a, double b) { - return a < b ? a : b; + if (PlatformDetector.isJavaScript()) { + return minImpl(a, b); + } + if (a != a) { + return a; + } + if (a == 0.0 && b == 0.0 && 1 / b == Double.NEGATIVE_INFINITY) { + return b; + } + return a <= b ? a : b; } + @GeneratedBy(MathNativeGenerator.class) + @NoSideEffects + private static native float maxImpl(double a, double b); + public static double max(double a, double b) { - return a > b ? a : b; + if (PlatformDetector.isJavaScript()) { + return maxImpl(a, b); + } + if (a != a) { + return a; + } + if (a == 0.0 && b == 0.0 && 1 / a == Double.NEGATIVE_INFINITY) { + return b; + } + return a >= b ? a : b; } + @GeneratedBy(MathNativeGenerator.class) + @NoSideEffects + private static native float minImpl(float a, float b); + public static float min(float a, float b) { - return a < b ? a : b; + if (PlatformDetector.isJavaScript()) { + return minImpl(a, b); + } + if (a != a) { + return a; + } + if (a == 0 && b == 0 && 1 / b == Float.NEGATIVE_INFINITY) { + return b; + } + return a <= b ? a : b; } + @GeneratedBy(MathNativeGenerator.class) + @NoSideEffects + private static native float maxImpl(float a, float b); + public static float max(float a, float b) { - return a > b ? a : b; + if (PlatformDetector.isJavaScript()) { + return maxImpl(a, b); + } + if (a != a) { + return a; + } + if (a == 0 && b == 0 && 1 / a == Float.NEGATIVE_INFINITY) { + return b; + } + return a >= b ? a : b; } public static int abs(int n) { diff --git a/tests/src/test/java/org/teavm/classlib/java/lang/MathTest.java b/tests/src/test/java/org/teavm/classlib/java/lang/MathTest.java index 14dcad3d1..cd5275bd7 100644 --- a/tests/src/test/java/org/teavm/classlib/java/lang/MathTest.java +++ b/tests/src/test/java/org/teavm/classlib/java/lang/MathTest.java @@ -16,13 +16,20 @@ package org.teavm.classlib.java.lang; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; import org.junit.Test; import org.junit.runner.RunWith; import org.teavm.junit.TeaVMTestRunner; @RunWith(TeaVMTestRunner.class) public class MathTest { + private static void sameDouble(double a, double b) { + assertEquals(Double.valueOf(a), Double.valueOf(b)); + } + + private static void sameFloat(float a, float b) { + assertEquals(Float.valueOf(a), Float.valueOf(b)); + } + @Test public void sinComputed() { assertEquals(0.90929742682568, Math.sin(2), 1E-14); @@ -62,52 +69,50 @@ public class MathTest { @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))); + sameFloat(Float.POSITIVE_INFINITY, Math.abs(Float.NEGATIVE_INFINITY)); + sameDouble(Double.POSITIVE_INFINITY, Math.abs(Double.NEGATIVE_INFINITY)); + sameDouble(5.0, Math.abs(-5.0)); + sameDouble(3.0, Math.abs(3.0)); + sameDouble(3.0, Math.abs(-3.0)); + sameDouble(5.0, Math.abs(5.0)); + sameFloat(0.0f, Math.abs(-0.0f)); + sameFloat(0.0f, Math.abs(0.0f)); + sameDouble(0.0, Math.abs(-0.0)); + sameDouble(0.0, Math.abs(0.0)); } @Test public void signumWorks() { - assertEquals(Double.valueOf(1.0), Double.valueOf(Math.signum(3.0))); - assertEquals(Double.valueOf(-1.0), Double.valueOf(Math.signum(-4.0))); - assertEquals(Float.valueOf(1f), Float.valueOf(Math.signum(3f))); - assertEquals(Float.valueOf(-1f), Float.valueOf(Math.signum(-4f))); + sameDouble(1.0, Math.signum(3.0)); + sameDouble(-1.0, Math.signum(-4.0)); + sameFloat(1f, Math.signum(3f)); + sameFloat(-1f, Math.signum(-4f)); - 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))); + sameDouble(0.0, Math.signum(0.0)); + sameDouble(-0.0, Math.signum(-0.0)); + sameDouble(Double.NaN, Math.signum(Double.NaN)); + sameFloat(0.0f, Math.signum(0.0f)); + sameFloat(-0.0f, Math.signum(-0.0f)); + sameFloat(Float.NaN, Math.signum(Float.NaN)); + sameDouble(-1.0, Math.signum(-Double.MIN_VALUE)); + sameDouble(1.0, Math.signum(Double.MIN_VALUE)); - assertEquals(Float.valueOf(-1), Float.valueOf(Math.signum(Float.NEGATIVE_INFINITY))); - assertEquals(Float.valueOf(1), Float.valueOf(Math.signum(Float.POSITIVE_INFINITY))); + sameFloat((float) -1, Math.signum(Float.NEGATIVE_INFINITY)); + sameFloat(1F, Math.signum(Float.POSITIVE_INFINITY)); } @Test public void copySignWorks() { - assertEquals(Double.valueOf(1.0), Double.valueOf(Math.copySign(1.0, 0.0))); - assertEquals(Double.valueOf(-1.0), Double.valueOf(Math.copySign(1.0, -0.0))); - assertEquals(Double.valueOf(1.0), Double.valueOf(Math.copySign(1.0, Double.NaN))); - assertEquals(Double.valueOf(Double.NaN), Double.valueOf(Math.copySign(Double.NaN, -1.0))); - assertEquals(Double.valueOf(Double.POSITIVE_INFINITY), - Double.valueOf(Math.copySign(Double.NEGATIVE_INFINITY, 1.0))); - assertEquals(Float.valueOf(1.0f), Float.valueOf(Math.copySign(1.0f, 0.0f))); - assertEquals(Float.valueOf(-1.0f), Float.valueOf(Math.copySign(1.0f, -0.0f))); - assertEquals(Float.valueOf(1.0f), Float.valueOf(Math.copySign(1.0f, Float.NaN))); - assertEquals(Float.valueOf(Float.NaN), Float.valueOf(Math.copySign(Float.NaN, -1.0f))); - assertEquals(Float.valueOf(Float.POSITIVE_INFINITY), - Float.valueOf(Math.copySign(Float.NEGATIVE_INFINITY, 1.0f))); + sameDouble(1.0, Math.copySign(1.0, 0.0)); + sameDouble(-1.0, Math.copySign(1.0, -0.0)); + sameDouble(1.0, Math.copySign(1.0, Double.NaN)); + sameDouble(Double.NaN, Math.copySign(Double.NaN, -1.0)); + sameDouble(Double.POSITIVE_INFINITY, Math.copySign(Double.NEGATIVE_INFINITY, 1.0)); + sameFloat(1.0f, Math.copySign(1.0f, 0.0f)); + sameFloat(-1.0f, Math.copySign(1.0f, -0.0f)); + sameFloat(1.0f, Math.copySign(1.0f, Float.NaN)); + sameFloat(Float.NaN, Math.copySign(Float.NaN, -1.0f)); + sameFloat(Float.POSITIVE_INFINITY, Math.copySign(Float.NEGATIVE_INFINITY, 1.0f)); } @Test @@ -120,34 +125,34 @@ public class MathTest { @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(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(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))); + sameDouble(-Double.MIN_VALUE, Math.nextDown(0.0)); + sameDouble(Double.MIN_VALUE, Math.nextUp(0.0)); + sameDouble(-Double.MIN_VALUE, Math.nextDown(-0.0)); + sameDouble(Double.MIN_VALUE, Math.nextUp(-0.0)); + sameFloat(-Float.MIN_VALUE, Math.nextDown(0.0f)); + sameFloat(Float.MIN_VALUE, Math.nextUp(0.0f)); + sameFloat(-Float.MIN_VALUE, Math.nextDown(-0.0f)); + sameFloat(Float.MIN_VALUE, Math.nextUp(-0.0f)); + sameDouble(0.10000000000000002, Math.nextUp(0.1)); + sameDouble(0.9999999999999999, Math.nextDown(1.0)); + sameDouble(-0.09999999999999999, Math.nextUp(-0.1)); + sameDouble(-1.0000000000000002, Math.nextDown(-1.0)); + sameFloat(0.10000001f, Math.nextUp(0.1f)); + sameFloat(0.99999994f, Math.nextDown(1.0f)); + sameFloat(-0.099999994f, Math.nextUp(-0.1f)); + sameFloat(-1.0000001f, Math.nextDown(-1.0f)); + sameFloat(Float.NEGATIVE_INFINITY, Math.nextDown(Float.NEGATIVE_INFINITY)); + sameFloat(Float.intBitsToFloat(Float.floatToIntBits(Float.POSITIVE_INFINITY) - 1), + Math.nextDown(Float.POSITIVE_INFINITY)); + sameFloat(Float.POSITIVE_INFINITY, Math.nextUp(Float.POSITIVE_INFINITY)); + sameFloat(Float.intBitsToFloat(Float.floatToIntBits(Float.NEGATIVE_INFINITY) - 1), + Math.nextUp(Float.NEGATIVE_INFINITY)); + sameDouble(Double.NEGATIVE_INFINITY, Math.nextDown(Double.NEGATIVE_INFINITY)); + sameDouble(Double.longBitsToDouble(Double.doubleToLongBits(Double.POSITIVE_INFINITY) - 1), + Math.nextDown(Double.POSITIVE_INFINITY)); + sameDouble(Double.POSITIVE_INFINITY, Math.nextUp(Double.POSITIVE_INFINITY)); + sameDouble(Double.longBitsToDouble(Double.doubleToLongBits(Double.NEGATIVE_INFINITY) - 1), + Math.nextUp(Double.NEGATIVE_INFINITY)); } @Test @@ -165,4 +170,28 @@ public class MathTest { assertEquals(1024, Math.getExponent(Double.NEGATIVE_INFINITY)); assertEquals(1024, Math.getExponent(Double.NaN)); } + + @Test + public void minMax() { + sameDouble(-1.0, Math.max(-2.0, -1.0)); + sameDouble(-2.0, Math.min(-2.0, -1.0)); + sameDouble(Double.NaN, Math.min(Double.NaN, Double.POSITIVE_INFINITY)); + sameDouble(Double.NaN, Math.min(Double.POSITIVE_INFINITY, Double.NaN)); + sameDouble(Double.NaN, Math.max(Double.NaN, Double.POSITIVE_INFINITY)); + sameDouble(Double.NaN, Math.max(Double.POSITIVE_INFINITY, Double.NaN)); + sameDouble(-0.0, Math.min(-0.0, 0.0)); + sameDouble(-0.0, Math.min(0.0, -0.0)); + sameDouble(0.0, Math.max(-0.0, 0.0)); + sameDouble(0.0, Math.max(0.0, -0.0)); + sameFloat(-1.0f, Math.max(-2.0f, -1.0f)); + sameFloat(-2.0f, Math.min(-2.0f, -1.0f)); + sameFloat(Float.NaN, Math.min(Float.NaN, Float.POSITIVE_INFINITY)); + sameFloat(Float.NaN, Math.min(Float.POSITIVE_INFINITY, Float.NaN)); + sameFloat(Float.NaN, Math.max(Float.NaN, Float.POSITIVE_INFINITY)); + sameFloat(Float.NaN, Math.max(Float.POSITIVE_INFINITY, Float.NaN)); + sameFloat(-0.0f, Math.min(-0.0f, 0.0f)); + sameFloat(-0.0f, Math.min(0.0f, -0.0f)); + sameFloat(0.0f, Math.max(-0.0f, 0.0f)); + sameFloat(0.0f, Math.max(0.0f, -0.0f)); + } }