From 8277671376b7745f9f33a1dc1206cbb8d66a0cb2 Mon Sep 17 00:00:00 2001 From: Ivan Hetman Date: Wed, 20 Sep 2023 23:51:55 +0300 Subject: [PATCH] classlib: make difference between raw and normal floating points to int conversion --- .../org/teavm/classlib/java/lang/TDouble.java | 13 ++++++++----- .../java/org/teavm/classlib/java/lang/TFloat.java | 15 +++++++++------ .../teavm/classlib/java/nio/TByteBufferImpl.java | 4 ++-- .../org/teavm/backend/javascript/runtime.js | 8 ++++---- .../org/teavm/classlib/java/lang/DoubleTest.java | 15 ++++++++++++++- .../org/teavm/classlib/java/lang/FloatTest.java | 15 ++++++++++++++- .../teavm/classlib/java/nio/ByteBufferTest.java | 3 +++ 7 files changed, 54 insertions(+), 19 deletions(-) diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TDouble.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TDouble.java index f66fbcf6b..4a2e63fba 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TDouble.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TDouble.java @@ -252,15 +252,18 @@ public class TDouble extends TNumber implements TComparable { @Unmanaged public static native boolean isFinite(double v); - public static long doubleToRawLongBits(double value) { - return doubleToLongBits(value); - } - @InjectedBy(DoubleGenerator.class) @Import(name = "teavm_reinterpretDoubleToLong") @NoSideEffects @Unmanaged - public static native long doubleToLongBits(double value); + public static native long doubleToRawLongBits(double value); + + public static long doubleToLongBits(double value) { + if (isNaN(value)) { + return 0x7ff8000000000000L; + } + return doubleToRawLongBits(value); + } @InjectedBy(DoubleGenerator.class) @Import(name = "teavm_reinterpretLongToDouble") diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TFloat.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TFloat.java index fda618bf8..bf7d209fc 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TFloat.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TFloat.java @@ -256,15 +256,18 @@ public class TFloat extends TNumber implements TComparable { return compare(value, other.value); } - public static int floatToRawIntBits(float value) { - return floatToIntBits(value); - } - - @JSBody(params = "value", script = "return $rt_floatToIntBits(value);") + @JSBody(params = "value", script = "return $rt_floatToRawIntBits(value);") @Import(name = "teavm_reinterpretFloatToInt") @NoSideEffects @Unmanaged - public static native int floatToIntBits(float value); + public static native int floatToRawIntBits(float value); + + public static int floatToIntBits(float value) { + if (isNaN(value)) { + return 0x7fc00000; + } + return floatToRawIntBits(value); + } @JSBody(params = "bits", script = "return $rt_intBitsToFloat(bits);") @Import(name = "teavm_reinterpretIntToFloat") diff --git a/classlib/src/main/java/org/teavm/classlib/java/nio/TByteBufferImpl.java b/classlib/src/main/java/org/teavm/classlib/java/nio/TByteBufferImpl.java index e24b2792d..639179afd 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/nio/TByteBufferImpl.java +++ b/classlib/src/main/java/org/teavm/classlib/java/nio/TByteBufferImpl.java @@ -379,7 +379,7 @@ class TByteBufferImpl extends TByteBuffer { @Override public TByteBuffer putDouble(double value) { - return putLong(Double.doubleToLongBits(value)); + return putLong(Double.doubleToRawLongBits(value)); } @Override @@ -389,7 +389,7 @@ class TByteBufferImpl extends TByteBuffer { @Override public TByteBuffer putDouble(int index, double value) { - return putLong(index, Double.doubleToLongBits(value)); + return putLong(index, Double.doubleToRawLongBits(value)); } @Override diff --git a/core/src/main/resources/org/teavm/backend/javascript/runtime.js b/core/src/main/resources/org/teavm/backend/javascript/runtime.js index 536a13e31..71bd1ebef 100644 --- a/core/src/main/resources/org/teavm/backend/javascript/runtime.js +++ b/core/src/main/resources/org/teavm/backend/javascript/runtime.js @@ -658,10 +658,10 @@ function $rt_eraseClinit(target) { var $rt_numberConversionView = new DataView(new ArrayBuffer(8)); -var $rt_doubleToLongBits; +var $rt_doubleToRawLongBits; var $rt_longBitsToDouble; if (typeof BigInt !== 'function') { - $rt_doubleToLongBits = function(n) { + $rt_doubleToRawLongBits = function(n) { $rt_numberConversionView.setFloat64(0, n, true); return new Long($rt_numberConversionView.getInt32(0, true), $rt_numberConversionView.getInt32(4, true)); } @@ -671,7 +671,7 @@ if (typeof BigInt !== 'function') { return $rt_numberConversionView.getFloat64(0, true); } } else { - $rt_doubleToLongBits = function(n) { + $rt_doubleToRawLongBits = function(n) { $rt_numberConversionView.setFloat64(0, n, true); // For compatibility with Safari var lo = $rt_numberConversionView.getInt32(0, true); @@ -688,7 +688,7 @@ if (typeof BigInt !== 'function') { } } -function $rt_floatToIntBits(n) { +function $rt_floatToRawIntBits(n) { $rt_numberConversionView.setFloat32(0, n); return $rt_numberConversionView.getInt32(0); } diff --git a/tests/src/test/java/org/teavm/classlib/java/lang/DoubleTest.java b/tests/src/test/java/org/teavm/classlib/java/lang/DoubleTest.java index b7cf451a8..54bf1dd78 100644 --- a/tests/src/test/java/org/teavm/classlib/java/lang/DoubleTest.java +++ b/tests/src/test/java/org/teavm/classlib/java/lang/DoubleTest.java @@ -25,6 +25,8 @@ import org.teavm.junit.TeaVMTestRunner; @RunWith(TeaVMTestRunner.class) public class DoubleTest { + public static final double OTHER_NAN = Double.longBitsToDouble(Double.doubleToLongBits(Double.NaN) + 1); + @Test public void parsed() { assertEquals(23, Double.parseDouble("23"), 1E-12); @@ -60,7 +62,6 @@ public class DoubleTest { public void testEquals() { assertNotEquals(Double.valueOf(-0.0), Double.valueOf(0.0)); assertEquals(Double.valueOf(3.0), Double.valueOf(3.0)); - assertEquals(Double.valueOf(Double.NaN), Double.valueOf(Double.NaN)); assertEquals(Double.valueOf(Double.POSITIVE_INFINITY), Double.valueOf(Double.POSITIVE_INFINITY)); assertNotEquals(Double.valueOf(Double.NEGATIVE_INFINITY), Double.valueOf(Double.POSITIVE_INFINITY)); assertEquals(Double.valueOf(Double.NEGATIVE_INFINITY), Double.valueOf(Double.NEGATIVE_INFINITY)); @@ -144,4 +145,16 @@ public class DoubleTest { assertEquals(1, Double.compare(0.0, -0.0)); assertEquals(-1, Double.compare(-0.0, 0.0)); } + + @Test + public void testNaN() { + assertTrue(Double.isNaN(OTHER_NAN)); + assertTrue(OTHER_NAN != OTHER_NAN); + assertTrue(OTHER_NAN != Double.NaN); + assertEquals(Double.valueOf(Double.NaN), Double.valueOf(Double.NaN)); + assertEquals(Double.valueOf(OTHER_NAN), Double.valueOf(Double.NaN)); + assertEquals(Double.valueOf(OTHER_NAN), Double.valueOf(OTHER_NAN)); + assertNotEquals(Double.doubleToRawLongBits(OTHER_NAN), Double.doubleToRawLongBits(Double.NaN)); + assertEquals(Double.doubleToLongBits(OTHER_NAN), Double.doubleToLongBits(Double.NaN)); + } } diff --git a/tests/src/test/java/org/teavm/classlib/java/lang/FloatTest.java b/tests/src/test/java/org/teavm/classlib/java/lang/FloatTest.java index 4a8498d31..73f1b6559 100644 --- a/tests/src/test/java/org/teavm/classlib/java/lang/FloatTest.java +++ b/tests/src/test/java/org/teavm/classlib/java/lang/FloatTest.java @@ -24,6 +24,8 @@ import org.teavm.junit.TeaVMTestRunner; @RunWith(TeaVMTestRunner.class) public class FloatTest { + private static final float OTHER_NAN = Float.intBitsToFloat(Float.floatToIntBits(Float.NaN) + 1); + @Test public void parsed() { assertEquals(23, Float.parseFloat("23"), 1E-12F); @@ -58,7 +60,6 @@ public class FloatTest { public void testEquals() { assertNotEquals(Float.valueOf(-0.0f), Float.valueOf(0.0f)); assertEquals(Float.valueOf(5.0f), Float.valueOf(5.0f)); - assertEquals(Float.valueOf(Float.NaN), Float.valueOf(Float.NaN)); assertEquals(Float.valueOf(Float.POSITIVE_INFINITY), Float.valueOf(Float.POSITIVE_INFINITY)); assertNotEquals(Float.valueOf(Float.NEGATIVE_INFINITY), Float.valueOf(Float.POSITIVE_INFINITY)); assertEquals(Float.valueOf(Float.NEGATIVE_INFINITY), Float.valueOf(Float.NEGATIVE_INFINITY)); @@ -126,4 +127,16 @@ public class FloatTest { assertEquals(1, Float.compare(0.0f, -0.0f)); assertEquals(-1, Float.compare(-0.0f, 0.0f)); } + + @Test + public void testNaN() { + assertTrue(Float.isNaN(OTHER_NAN)); + assertTrue(OTHER_NAN != OTHER_NAN); + assertTrue(OTHER_NAN != Double.NaN); + assertEquals(Float.valueOf(Float.NaN), Float.valueOf(Float.NaN)); + assertEquals(Float.valueOf(OTHER_NAN), Float.valueOf(Float.NaN)); + assertEquals(Float.valueOf(OTHER_NAN), Float.valueOf(OTHER_NAN)); + assertNotEquals(Float.floatToRawIntBits(OTHER_NAN), Float.floatToRawIntBits(Float.NaN)); + assertEquals(Float.floatToIntBits(OTHER_NAN), Float.floatToIntBits(Float.NaN)); + } } diff --git a/tests/src/test/java/org/teavm/classlib/java/nio/ByteBufferTest.java b/tests/src/test/java/org/teavm/classlib/java/nio/ByteBufferTest.java index 3723fe57c..999d3dcd5 100644 --- a/tests/src/test/java/org/teavm/classlib/java/nio/ByteBufferTest.java +++ b/tests/src/test/java/org/teavm/classlib/java/nio/ByteBufferTest.java @@ -29,6 +29,7 @@ import java.nio.InvalidMarkException; import java.nio.ReadOnlyBufferException; import org.junit.Test; import org.junit.runner.RunWith; +import org.teavm.classlib.java.lang.DoubleTest; import org.teavm.junit.TeaVMTestRunner; import org.teavm.junit.WholeClassCompilation; @@ -632,6 +633,8 @@ public class ByteBufferTest { buffer.putDouble(1, 2.0); assertArrayEquals(new byte[] { 63, 64, 0, 0, 0, 0, 0, 0, 0, 55, 0, 0, 0, 0, 0, 0 }, array); + buffer.putDouble(0, DoubleTest.OTHER_NAN); + assertArrayEquals(new byte[] { 127, -8, 0, 0, 0, 0, 0, 1, 0, 55, 0, 0, 0, 0, 0, 0 }, array); } @Test