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

Document JavaScript engine-specific handling of NaN values #989

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
25 changes: 25 additions & 0 deletions classlib/src/main/java/org/teavm/classlib/java/lang/TDouble.java
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,17 @@ public boolean isInfinite() {
@Import(name = "teavm_reinterpretDoubleToLong")
@NoSideEffects
@Unmanaged
/**
* Platform dependent behaviour in the JS backend.
*
* The JavaScript engines in Firefox and Safari have special handling of NaN values.
*
* Except for V8 in chrome, most engines seem to canonicalize NaN values for internal
* bookkeeping (and using these NaN values as object pointers internally). This is not
* expected to change in the future.
*
* @see #longBitsToDouble(double)
*/
public static native long doubleToRawLongBits(double value);

public static long doubleToLongBits(double value) {
Expand All @@ -281,6 +292,20 @@ public static long doubleToLongBits(double value) {
@Import(name = "teavm_reinterpretLongToDouble")
@NoSideEffects
@Unmanaged
/**
* Platform dependent behaviour in the JS backend.
*
* The JavaScript engines in Firefox and Safari have special handling of NaN values.
*
* Except for V8 in chrome, most engines seem to canonicalize NaN values for internal
* bookkeeping (and using these NaN values as object pointers internally). This is not
* expected to change in the future.
*
* As a result, application code intending to run with the JS backend should not depend
* on `long`s retaining their bits through the conversion of a `double`.
*
* See https://github.com/konsoletyper/teavm/issues/895
*/
public static native double longBitsToDouble(long bits);

public static String toHexString(double d) {
Expand Down
26 changes: 26 additions & 0 deletions classlib/src/main/java/org/teavm/classlib/java/lang/TFloat.java
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,17 @@ public int compareTo(TFloat other) {
@Import(name = "teavm_reinterpretFloatToInt")
@NoSideEffects
@Unmanaged
/**
* Platform dependent behaviour in the JS backend.
*
* The JavaScript engines in Firefox and Safari have special handling of NaN values.
*
* Except for V8 in chrome, most engines seem to canonicalize NaN values for internal
* bookkeeping (and using these NaN values as object pointers internally). This is not
* expected to change in the future.
*
* @see #intBitsToFloat(float)
*/
public static native int floatToRawIntBits(float value);

public static int floatToIntBits(float value) {
Expand All @@ -277,6 +288,21 @@ public static int floatToIntBits(float value) {
@Import(name = "teavm_reinterpretIntToFloat")
@NoSideEffects
@Unmanaged
/**
* Platform dependent behaviour in the JS backend.
*
* The JavaScript engines in Firefox and Safari have special handling of NaN values.
*
* Except for V8 in chrome, most engines seem to canonicalize NaN values for internal
* bookkeeping (and using these NaN values as object pointers internally). This is not
* expected to change in the future.
*
* As a result, application code intending to run with the JS backend should not depend
* on `long`s retaining their bits through the conversion of a `double` — or `float`
* for that matter.
*
* See https://github.com/konsoletyper/teavm/issues/895
*/
public static native float intBitsToFloat(int bits);

public static String toHexString(float f) {
Expand Down
15 changes: 12 additions & 3 deletions tests/src/test/java/org/teavm/classlib/java/lang/DoubleTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -150,15 +150,24 @@ public void compares() {
assertEquals(-1, Double.compare(-0.0, 0.0));
}

/**
* This test will fail on the JavaScript engines in Firefox and Safari.
*
* This is due to how JS numbers (i.e. `double`s) are handled internally. Except for V8 in chrome,
* most engines seem to canonicalize NaN values for internal bookkeeping (and using these NaN values
* as object pointers internally). This is not expected to change in the future.
*
* See https://github.com/konsoletyper/teavm/issues/895
*/
@Test
public void testNaN() {
assertTrue(Double.isNaN(OTHER_NAN));
assertTrue(OTHER_NAN != OTHER_NAN);
assertTrue(OTHER_NAN != Double.NaN);
assertTrue(OTHER_NAN != OTHER_NAN); // fail
assertTrue(OTHER_NAN != Double.NaN); // fail
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));
assertNotEquals(Double.doubleToRawLongBits(OTHER_NAN), Double.doubleToRawLongBits(Double.NaN)); // fail
assertEquals(Double.doubleToLongBits(OTHER_NAN), Double.doubleToLongBits(Double.NaN));
}
}
15 changes: 12 additions & 3 deletions tests/src/test/java/org/teavm/classlib/java/lang/FloatTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -132,15 +132,24 @@ public void compares() {
assertEquals(-1, Float.compare(-0.0f, 0.0f));
}

/**
* This test will fail on the JavaScript engines in Firefox and Safari.
*
* This is due to how JS numbers (i.e. `double`s) are handled internally. Except for V8 in chrome,
* most engines seem to canonicalize NaN values for internal bookkeeping (and using these NaN values
* as object pointers internally). This is not expected to change in the future.
*
* See https://github.com/konsoletyper/teavm/issues/895
*/
@Test
public void testNaN() {
assertTrue(Float.isNaN(OTHER_NAN));
assertTrue(OTHER_NAN != OTHER_NAN);
assertTrue(OTHER_NAN != Double.NaN);
assertTrue(OTHER_NAN != OTHER_NAN); // fail
assertTrue(OTHER_NAN != Double.NaN); // fail
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));
assertNotEquals(Float.floatToRawIntBits(OTHER_NAN), Float.floatToRawIntBits(Float.NaN)); // fail
assertEquals(Float.floatToIntBits(OTHER_NAN), Float.floatToIntBits(Float.NaN));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
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;

@RunWith(TeaVMTestRunner.class)
Expand Down Expand Up @@ -631,8 +630,6 @@ public void putsDouble() {

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
Expand Down
Loading