diff --git a/teavm-classlib/pom.xml b/teavm-classlib/pom.xml
index 845a04d64..e242077f1 100644
--- a/teavm-classlib/pom.xml
+++ b/teavm-classlib/pom.xml
@@ -37,7 +37,7 @@
process-test-classes
- true
+ false
diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/DoubleNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/DoubleNativeGenerator.java
new file mode 100644
index 000000000..fdafa5fa2
--- /dev/null
+++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/DoubleNativeGenerator.java
@@ -0,0 +1,35 @@
+package org.teavm.classlib.java.lang;
+
+import java.io.IOException;
+import org.teavm.codegen.SourceWriter;
+import org.teavm.javascript.ni.Generator;
+import org.teavm.javascript.ni.GeneratorContext;
+import org.teavm.model.MethodReference;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+public class DoubleNativeGenerator implements Generator {
+ @Override
+ public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException {
+ switch (methodRef.getName()) {
+ case "isNaN":
+ generateIsNaN(context, writer);
+ break;
+ case "isInfinite":
+ generateIsInfinite(context, writer);
+ break;
+ }
+ }
+
+ private void generateIsNaN(GeneratorContext context, SourceWriter writer) throws IOException {
+ writer.append("return (isNaN(").append(context.getParameterName(1)).append(")").ws().append("?")
+ .ws().append("1").ws().append(":").ws().append("0").ws().append(");").softNewLine();
+ }
+
+ private void generateIsInfinite(GeneratorContext context, SourceWriter writer) throws IOException {
+ writer.append("return (isFinite(").append(context.getParameterName(1)).append(")").ws().append("?")
+ .ws().append("0").ws().append(":").ws().append("1").append(");").softNewLine();
+ }
+}
diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TAbstractStringBuilder.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TAbstractStringBuilder.java
index 161dd5b9b..59471d8c5 100644
--- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TAbstractStringBuilder.java
+++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TAbstractStringBuilder.java
@@ -12,13 +12,24 @@ import org.teavm.javascript.ni.Rename;
*/
class TAbstractStringBuilder extends TObject implements TSerializable, TCharSequence {
private static final float[] powersOfTen = { 1E1f, 1E2f, 1E4f, 1E8f, 1E16f, 1E32f };
+ private static final double[] doublePowersOfTen = { 1E1, 1E2, 1E4, 1E8, 1E16, 1E32, 1E64, 1E128, 1E256 };
private static final float[] negPowersOfTen = { 1E-1f, 1E-2f, 1E-4f, 1E-8f, 1E-16f, 1E-32f };
+ private static final double[] negDoublePowersOfTen = { 1E-1, 1E-2, 1E-4, 1E-8, 1E-16, 1E-32,
+ 1E-64, 1E-128, 1E-256 };
private static final int[] intPowersOfTen = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000,
1000000000 };
+ private static final long[] longPowersOfTen = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000,
+ 1000000000, 10000000000L, 100000000000L, 1000000000000L, 10000000000000L, 100000000000000L,
+ 1000000000000000L, 10000000000000000L, 100000000000000000L, 1000000000000000000L };
+ private static final long[] longLogPowersOfTen = { 1, 10, 100, 10000, 100000000, 10000000000000000L, };
private static final int FLOAT_DECIMAL_PRECISION = 7;
+ private static final int DOUBLE_DECIMAL_PRECISION = 16;
private static final float FLOAT_DECIMAL_FACTOR = 1E6f;
+ private static final double DOUBLE_DECIMAL_FACTOR = 1E15f;
private static final int FLOAT_MAX_EXPONENT = 38;
+ private static final int DOUBLE_MAX_EXPONENT = 308;
private static final int FLOAT_MAX_POS = 1000000;
+ private static final long DOUBLE_MAX_POS = 1000000000000000L;
char[] buffer;
int length;
@@ -128,13 +139,13 @@ class TAbstractStringBuilder extends TObject implements TSerializable, TCharSequ
buffer[length++] = '.';
buffer[length++] = '0';
return this;
- } else if (Float.isNaN(value)) {
+ } else if (TFloat.isNaN(value)) {
ensureCapacity(length + 3);
buffer[length++] = 'N';
buffer[length++] = 'a';
buffer[length++] = 'N';
return this;
- } else if (Float.isInfinite(value)) {
+ } else if (TFloat.isInfinite(value)) {
if (value > 0) {
ensureCapacity(8);
} else {
@@ -237,7 +248,6 @@ class TAbstractStringBuilder extends TObject implements TSerializable, TCharSequ
intDigit = 0;
}
buffer[length++] = (char)('0' + intDigit);
- value = (value - intDigit) * 10;
if (--intPart == 0) {
buffer[length++] = '.';
}
@@ -259,6 +269,157 @@ class TAbstractStringBuilder extends TObject implements TSerializable, TCharSequ
return this;
}
+ protected TAbstractStringBuilder append(double value) {
+ if (value == 0) {
+ ensureCapacity(length + 3);
+ buffer[length++] = '0';
+ buffer[length++] = '.';
+ buffer[length++] = '0';
+ return this;
+ } else if (value == -0) {
+ ensureCapacity(length + 4);
+ buffer[length++] = '-';
+ buffer[length++] = '0';
+ buffer[length++] = '.';
+ buffer[length++] = '0';
+ return this;
+ } else if (TDouble.isNaN(value)) {
+ ensureCapacity(length + 3);
+ buffer[length++] = 'N';
+ buffer[length++] = 'a';
+ buffer[length++] = 'N';
+ return this;
+ } else if (TDouble.isInfinite(value)) {
+ if (value > 0) {
+ ensureCapacity(8);
+ } else {
+ ensureCapacity(9);
+ buffer[length++] = '-';
+ }
+ buffer[length++] = 'I';
+ buffer[length++] = 'n';
+ buffer[length++] = 'f';
+ buffer[length++] = 'i';
+ buffer[length++] = 'n';
+ buffer[length++] = 'i';
+ buffer[length++] = 't';
+ buffer[length++] = 'y';
+ return this;
+ }
+ // Get absolute value
+ boolean negative = false;
+ int sz = 1; // Decimal point always included
+ if (value < 0) {
+ negative = true;
+ value = -value;
+ ++sz; // including '-' sign of mantissa
+ }
+
+ // Split into decimal mantissa and decimal exponent
+ int exp = 0;
+ long mantissa = 0;
+ int intPart = 1;
+ int digits = 0;
+ if (value >= 1) {
+ int bit = 256;
+ exp = 0;
+ double digit = 1;
+ for (int i = doublePowersOfTen.length - 1; i >= 0; --i) {
+ if ((exp | bit) <= DOUBLE_MAX_EXPONENT && doublePowersOfTen[i] * digit < value) {
+ digit *= doublePowersOfTen[i];
+ exp |= bit;
+ }
+ bit >>= 1;
+ }
+ mantissa = (long)((value / (digit / DOUBLE_DECIMAL_FACTOR)) + 0.5f);
+ } else {
+ ++sz;
+ int bit = 256;
+ exp = 0;
+ double digit = 1;
+ for (int i = negDoublePowersOfTen.length - 1; i >= 0; --i) {
+ if ((exp | bit) <= DOUBLE_MAX_EXPONENT && negDoublePowersOfTen[i] * digit * 10 > value) {
+ digit *= negPowersOfTen[i];
+ exp |= bit;
+ }
+ bit >>= 1;
+ }
+ exp = -exp;
+ mantissa = (long)(((value * DOUBLE_MAX_POS) / digit) + 0.5f);
+ }
+
+ // Remove trailing zeros
+ digits = DOUBLE_DECIMAL_PRECISION;
+ int zeros = trailingDecimalZeros(mantissa);
+ if (zeros > 0) {
+ digits -= zeros;
+ }
+
+ // Handle special case of exponent close to 0
+ if (exp < 7 && exp >= -3) {
+ if (exp >= 0) {
+ intPart = exp + 1;
+ digits = Math.max(digits, intPart + 1);
+ exp = 0;
+ } else if (exp < 0) {
+ mantissa /= longPowersOfTen[-exp];
+ digits -= exp;
+ exp = 0;
+ }
+ }
+ sz += digits;
+
+ // Extend buffer to store exponent
+ if (exp != 0) {
+ sz += 2;
+ if (exp < 10 || exp > 10) {
+ ++sz;
+ }
+ if (exp < 100 || exp > 100) {
+ ++sz;
+ }
+ }
+
+ // Print mantissa
+ ensureCapacity(length + sz);
+ if (negative) {
+ buffer[length++] = '-';
+ }
+ long pos = DOUBLE_MAX_POS;
+ for (int i = 0; i < digits; ++i) {
+ int intDigit;
+ if (pos > 0) {
+ intDigit = (int)(mantissa / pos);
+ mantissa %= pos;
+ } else {
+ intDigit = 0;
+ }
+ buffer[length++] = (char)('0' + intDigit);
+ if (--intPart == 0) {
+ buffer[length++] = '.';
+ }
+ pos /= 10;
+ }
+
+ // Print exponent
+ if (exp != 0) {
+ buffer[length++] = 'E';
+ if (exp < 0) {
+ exp = -exp;
+ buffer[length++] = '-';
+ }
+ if (exp > 100) {
+ buffer[length++] = (char)('0' + exp / 100);
+ exp %= 100;
+ }
+ if (exp > 10) {
+ buffer[length++] = (char)('0' + exp / 10);
+ }
+ buffer[length++] = (char)('0' + exp % 10);
+ }
+ return this;
+ }
+
private static int trailingDecimalZeros(int n) {
if (n % 1000000000 == 0) {
return 9;
@@ -283,6 +444,20 @@ class TAbstractStringBuilder extends TObject implements TSerializable, TCharSequ
return result;
}
+ private static int trailingDecimalZeros(long n) {
+ long zeros = 1;
+ int result = 0;
+ int bit = 16;
+ for (int i = longLogPowersOfTen.length - 1; i >= 0; --i) {
+ if (n % zeros == 0) {
+ result |= bit;
+ zeros *= longLogPowersOfTen[i];
+ }
+ bit >>>= 1;
+ }
+ return result;
+ }
+
protected TAbstractStringBuilder append(char c) {
ensureCapacity(length + 1);
buffer[length++] = c;
diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TDouble.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TDouble.java
new file mode 100644
index 000000000..78f4dc2d7
--- /dev/null
+++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TDouble.java
@@ -0,0 +1,15 @@
+package org.teavm.classlib.java.lang;
+
+import org.teavm.javascript.ni.GeneratedBy;
+
+/**
+ *
+ * @author Alexey Andreev
+ */
+public class TDouble {
+ @GeneratedBy(DoubleNativeGenerator.class)
+ public static native boolean isNaN(double v);
+
+ @GeneratedBy(DoubleNativeGenerator.class)
+ public static native boolean isInfinite(double v);
+}
diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TStringBuilder.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TStringBuilder.java
index 1c06c7634..2b6b854a4 100644
--- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TStringBuilder.java
+++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TStringBuilder.java
@@ -29,6 +29,12 @@ public class TStringBuilder extends TAbstractStringBuilder {
return this;
}
+ @Override
+ protected TStringBuilder append(double value) {
+ super.append(value);
+ return this;
+ }
+
@Override
public TStringBuilder append(char c) {
super.append(c);
diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/StringBuilderTest.java b/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/StringBuilderTest.java
index 11b541a47..a7cb5d611 100644
--- a/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/StringBuilderTest.java
+++ b/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/StringBuilderTest.java
@@ -190,6 +190,13 @@ public class StringBuilderTest {
assertEquals("-Infinity", sb.toString());
}
+ @Test
+ public void doubleAppended() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(1.2345678E150);
+ assertEquals("1.2345678E150", sb.toString());
+ }
+
@Test
public void appendsCodePoint() {
StringBuilder sb = new StringBuilder();