mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-31 12:24:10 -08:00
Improve double to string conversion
This commit is contained in:
parent
1cf3cce076
commit
f23c71cd97
|
@ -0,0 +1,197 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.teavm.classlib.impl.text;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public final class DoubleAnalyzer {
|
||||||
|
private DoubleAnalyzer() {
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final int MAX_ABS_DEC_EXP = 330;
|
||||||
|
public static final int DECIMAL_PRECISION = 18;
|
||||||
|
public static final long DOUBLE_MAX_POS = 100000000000000000L;
|
||||||
|
private static long[] mantissa10Table = new long[MAX_ABS_DEC_EXP * 2];
|
||||||
|
private static int[] exp10Table = new int[MAX_ABS_DEC_EXP * 2];
|
||||||
|
|
||||||
|
static {
|
||||||
|
long decimalMantissaOne = 8000000000000000000L;
|
||||||
|
|
||||||
|
long mantissa = decimalMantissaOne;
|
||||||
|
long remainder = 0;
|
||||||
|
int exponent = 1023;
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_ABS_DEC_EXP; ++i) {
|
||||||
|
mantissa10Table[i + MAX_ABS_DEC_EXP] = Long.divideUnsigned(mantissa, 80);
|
||||||
|
exp10Table[i + MAX_ABS_DEC_EXP] = exponent;
|
||||||
|
|
||||||
|
mantissa = Long.divideUnsigned(mantissa, 10);
|
||||||
|
remainder = Long.remainderUnsigned(mantissa, 10);
|
||||||
|
while (mantissa <= decimalMantissaOne && (mantissa & (1L << 63)) == 0) {
|
||||||
|
mantissa <<= 1;
|
||||||
|
exponent++;
|
||||||
|
remainder <<= 1;
|
||||||
|
}
|
||||||
|
mantissa += remainder / 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
long maxMantissa = Long.MAX_VALUE / 10;
|
||||||
|
mantissa = decimalMantissaOne;
|
||||||
|
exponent = 1023;
|
||||||
|
for (int i = 0; i < MAX_ABS_DEC_EXP; ++i) {
|
||||||
|
long nextMantissa = mantissa;
|
||||||
|
int shift = 0;
|
||||||
|
while (nextMantissa > maxMantissa) {
|
||||||
|
nextMantissa >>= 1;
|
||||||
|
shift++;
|
||||||
|
exponent--;
|
||||||
|
}
|
||||||
|
|
||||||
|
nextMantissa *= 10;
|
||||||
|
if (shift > 0) {
|
||||||
|
long shiftedOffPart = mantissa & ((1 << shift) - 1);
|
||||||
|
nextMantissa += (shiftedOffPart * 10) >> shift;
|
||||||
|
}
|
||||||
|
mantissa = nextMantissa;
|
||||||
|
|
||||||
|
mantissa10Table[MAX_ABS_DEC_EXP - i - 1] = Long.divideUnsigned(mantissa, 80);
|
||||||
|
exp10Table[MAX_ABS_DEC_EXP - i - 1] = exponent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void analyze(double d, Result result) {
|
||||||
|
long bits = Double.doubleToLongBits(d);
|
||||||
|
result.sign = (bits & (1L << 63)) != 0;
|
||||||
|
long mantissa = bits & ((1L << 52) - 1);
|
||||||
|
int exponent = (int) (bits >> 52) & ((1 << 11) - 1);
|
||||||
|
if (mantissa == 0 && exponent == 0) {
|
||||||
|
result.mantissa = 0;
|
||||||
|
result.exponent = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int errorShift = 0;
|
||||||
|
if (exponent == 0) {
|
||||||
|
mantissa <<= 1;
|
||||||
|
while ((mantissa & (1L << 52)) == 0) {
|
||||||
|
mantissa <<= 1;
|
||||||
|
exponent--;
|
||||||
|
++errorShift;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mantissa |= 1L << 52;
|
||||||
|
}
|
||||||
|
|
||||||
|
int decExponent = Arrays.binarySearch(exp10Table, exponent);
|
||||||
|
if (decExponent < 0) {
|
||||||
|
decExponent = -decExponent - 2;
|
||||||
|
}
|
||||||
|
int binExponentCorrection = exponent - exp10Table[decExponent];
|
||||||
|
int mantissaShift = 12 + binExponentCorrection;
|
||||||
|
|
||||||
|
long decMantissa = mulAndShiftRight(mantissa, mantissa10Table[decExponent], mantissaShift);
|
||||||
|
if (decMantissa >= 1000000000000000000L) {
|
||||||
|
++decExponent;
|
||||||
|
binExponentCorrection = exponent - exp10Table[decExponent];
|
||||||
|
mantissaShift = 12 + binExponentCorrection;
|
||||||
|
decMantissa = mulAndShiftRight(mantissa, mantissa10Table[decExponent], mantissaShift);
|
||||||
|
}
|
||||||
|
|
||||||
|
long error = mantissa10Table[decExponent] >>> (63 - mantissaShift - errorShift);
|
||||||
|
long upError = (error + 1) >> 1;
|
||||||
|
long downError = error >> 1;
|
||||||
|
if (mantissa == (1L << 52)) {
|
||||||
|
downError >>= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
long lowerPos = findLowerDistanceToZero(decMantissa, downError);
|
||||||
|
long upperPos = findUpperDistanceToZero(decMantissa, upError);
|
||||||
|
if (lowerPos > upperPos) {
|
||||||
|
decMantissa = (decMantissa / lowerPos) * lowerPos;
|
||||||
|
} else if (lowerPos < upperPos) {
|
||||||
|
decMantissa = (decMantissa / upperPos) * upperPos + upperPos;
|
||||||
|
} else {
|
||||||
|
decMantissa = ((decMantissa + upperPos / 2) / upperPos) * upperPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (decMantissa >= 1000000000000000000L) {
|
||||||
|
decExponent++;
|
||||||
|
decMantissa /= 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.mantissa = decMantissa;
|
||||||
|
result.exponent = decExponent - MAX_ABS_DEC_EXP;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long findLowerDistanceToZero(long mantissa, long error) {
|
||||||
|
long pos = 10;
|
||||||
|
while (pos <= error) {
|
||||||
|
pos *= 10;
|
||||||
|
}
|
||||||
|
long mantissaRight = mantissa % pos;
|
||||||
|
if (mantissaRight >= error / 2) {
|
||||||
|
pos /= 10;
|
||||||
|
}
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long findUpperDistanceToZero(long mantissa, long error) {
|
||||||
|
long pos = 1;
|
||||||
|
while (pos <= error) {
|
||||||
|
pos *= 10;
|
||||||
|
}
|
||||||
|
long mantissaRight = mantissa % pos;
|
||||||
|
if (pos - mantissaRight > error / 2) {
|
||||||
|
pos /= 10;
|
||||||
|
}
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multiply two longs and shft result right by 64-shift bits.
|
||||||
|
private static long mulAndShiftRight(long a, long b, int shift) {
|
||||||
|
long a1 = a & 0xFFFF;
|
||||||
|
long a2 = (a >>> 16) & 0xFFFF;
|
||||||
|
long a3 = (a >>> 32) & 0xFFFF;
|
||||||
|
long a4 = (a >>> 48) & 0xFFFF;
|
||||||
|
|
||||||
|
long b1 = b & 0xFFFF;
|
||||||
|
long b2 = (b >>> 16) & 0xFFFF;
|
||||||
|
long b3 = (b >>> 32) & 0xFFFF;
|
||||||
|
long b4 = (b >>> 48) & 0xFFFF;
|
||||||
|
|
||||||
|
long cm = b3 * a1 + b2 * a2 + b1 * a3;
|
||||||
|
long c0 = b4 * a1 + b3 * a2 + b2 * a3 + b1 * a4;
|
||||||
|
long c1 = b4 * a2 + b3 * a3 + b2 * a4;
|
||||||
|
long c2 = b4 * a3 + b3 * a4;
|
||||||
|
long c3 = b4 * a4;
|
||||||
|
|
||||||
|
long c = (c3 << (32 + shift)) + (c2 << (16 + shift)) + (c1 << shift);
|
||||||
|
if (shift <= 16) {
|
||||||
|
c += c0 >>> (16 - shift);
|
||||||
|
} else {
|
||||||
|
c += c0 << (shift - 16);
|
||||||
|
}
|
||||||
|
c += cm >>> (32 - shift);
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Result {
|
||||||
|
public long mantissa;
|
||||||
|
public int exponent;
|
||||||
|
public boolean sign;
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,6 +28,17 @@ public class LongNativeGenerator implements Generator {
|
||||||
case "compare":
|
case "compare":
|
||||||
writer.append("return Long_compare(").append(context.getParameterName(1)).append(", ")
|
writer.append("return Long_compare(").append(context.getParameterName(1)).append(", ")
|
||||||
.append(context.getParameterName(2)).append(");").softNewLine();
|
.append(context.getParameterName(2)).append(");").softNewLine();
|
||||||
|
context.useLongLibrary();
|
||||||
|
break;
|
||||||
|
case "divideUnsigned":
|
||||||
|
writer.append("return Long_udiv(").append(context.getParameterName(1)).append(", ")
|
||||||
|
.append(context.getParameterName(2)).append(");").softNewLine();
|
||||||
|
context.useLongLibrary();
|
||||||
|
break;
|
||||||
|
case "remainderUnsigned":
|
||||||
|
writer.append("return Long_urem(").append(context.getParameterName(1)).append(", ")
|
||||||
|
.append(context.getParameterName(2)).append(");").softNewLine();
|
||||||
|
context.useLongLibrary();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,31 +15,27 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.classlib.java.lang;
|
package org.teavm.classlib.java.lang;
|
||||||
|
|
||||||
|
import org.teavm.classlib.impl.text.DoubleAnalyzer;
|
||||||
import org.teavm.classlib.java.io.TSerializable;
|
import org.teavm.classlib.java.io.TSerializable;
|
||||||
import org.teavm.classlib.java.util.TArrays;
|
import org.teavm.classlib.java.util.TArrays;
|
||||||
|
|
||||||
class TAbstractStringBuilder extends TObject implements TSerializable, TCharSequence {
|
class TAbstractStringBuilder extends TObject implements TSerializable, TCharSequence {
|
||||||
static class Constants {
|
static class Constants {
|
||||||
private static float[] powersOfTen = { 1E1f, 1E2f, 1E4f, 1E8f, 1E16f, 1E32f };
|
static float[] powersOfTen = { 1E1f, 1E2f, 1E4f, 1E8f, 1E16f, 1E32f };
|
||||||
private static double[] doublePowersOfTen = { 1E1, 1E2, 1E4, 1E8, 1E16, 1E32, 1E64, 1E128, 1E256 };
|
static float[] negPowersOfTen = { 1E-1f, 1E-2f, 1E-4f, 1E-8f, 1E-16f, 1E-32f };
|
||||||
private static float[] negPowersOfTen = { 1E-1f, 1E-2f, 1E-4f, 1E-8f, 1E-16f, 1E-32f };
|
static int[] intPowersOfTen = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000,
|
||||||
private static double[] negDoublePowersOfTen = { 1E-1, 1E-2, 1E-4, 1E-8, 1E-16, 1E-32,
|
|
||||||
1E-64, 1E-128, 1E-256 };
|
|
||||||
private static int[] intPowersOfTen = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000,
|
|
||||||
1000000000 };
|
1000000000 };
|
||||||
private static long[] longPowersOfTen = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000,
|
static long[] longPowersOfTen = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000,
|
||||||
1000000000, 10000000000L, 100000000000L, 1000000000000L, 10000000000000L, 100000000000000L,
|
1000000000, 10000000000L, 100000000000L, 1000000000000L, 10000000000000L, 100000000000000L,
|
||||||
1000000000000000L, 10000000000000000L, 100000000000000000L, 1000000000000000000L };
|
1000000000000000L, 10000000000000000L, 100000000000000000L, 1000000000000000000L };
|
||||||
private static final long[] longLogPowersOfTen = { 1, 10, 100, 10000, 100000000, 10000000000000000L, };
|
static final long[] longLogPowersOfTen = { 1, 10, 100, 10000, 100000000, 10000000000000000L, };
|
||||||
|
|
||||||
private static final int FLOAT_DECIMAL_PRECISION = 7;
|
static final int FLOAT_DECIMAL_PRECISION = 7;
|
||||||
private static final int DOUBLE_DECIMAL_PRECISION = 16;
|
static final float FLOAT_DECIMAL_FACTOR = 1E6f;
|
||||||
private static final float FLOAT_DECIMAL_FACTOR = 1E6f;
|
static final int FLOAT_MAX_EXPONENT = 38;
|
||||||
private static final double DOUBLE_DECIMAL_FACTOR = 1E15;
|
static final int FLOAT_MAX_POS = 1000000;
|
||||||
private static final int FLOAT_MAX_EXPONENT = 38;
|
|
||||||
private static final int DOUBLE_MAX_EXPONENT = 308;
|
static final DoubleAnalyzer.Result doubleAnalysisResult = new DoubleAnalyzer.Result();
|
||||||
private static final int FLOAT_MAX_POS = 1000000;
|
|
||||||
private static final long DOUBLE_MAX_POS = 1000000000000000L;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char[] buffer;
|
char[] buffer;
|
||||||
|
@ -388,58 +384,22 @@ class TAbstractStringBuilder extends TObject implements TSerializable, TCharSequ
|
||||||
buffer[target++] = 'y';
|
buffer[target++] = 'y';
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DoubleAnalyzer.Result number = Constants.doubleAnalysisResult;
|
||||||
|
DoubleAnalyzer.analyze(value, number);
|
||||||
|
long mantissa = number.mantissa;
|
||||||
|
int exp = number.exponent;
|
||||||
|
boolean negative = number.sign;
|
||||||
|
int intPart = 1;
|
||||||
|
|
||||||
// Get absolute value
|
// Get absolute value
|
||||||
boolean negative = false;
|
|
||||||
int sz = 1; // Decimal point always included
|
int sz = 1; // Decimal point always included
|
||||||
if (value < 0) {
|
if (negative) {
|
||||||
negative = true;
|
|
||||||
value = -value;
|
|
||||||
++sz; // including '-' sign of mantissa
|
++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 = Constants.doublePowersOfTen.length - 1; i >= 0; --i) {
|
|
||||||
if ((exp | bit) <= Constants.DOUBLE_MAX_EXPONENT && Constants.doublePowersOfTen[i] * digit <= value) {
|
|
||||||
digit *= Constants.doublePowersOfTen[i];
|
|
||||||
exp |= bit;
|
|
||||||
}
|
|
||||||
bit >>= 1;
|
|
||||||
}
|
|
||||||
mantissa = (long) (((value / digit) * Constants.DOUBLE_DECIMAL_FACTOR) + 0.5);
|
|
||||||
} else {
|
|
||||||
int bit = 256;
|
|
||||||
exp = 0;
|
|
||||||
double digit = 1;
|
|
||||||
for (int i = Constants.negDoublePowersOfTen.length - 1; i >= 0; --i) {
|
|
||||||
if ((exp | bit) <= 324 && Constants.negDoublePowersOfTen[i] * digit * 10 > value) {
|
|
||||||
exp |= bit;
|
|
||||||
if (exp == 324) {
|
|
||||||
value /= Constants.negDoublePowersOfTen[i];
|
|
||||||
} else {
|
|
||||||
digit *= Constants.negDoublePowersOfTen[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bit >>= 1;
|
|
||||||
}
|
|
||||||
exp = -exp;
|
|
||||||
mantissa = (long) (((value * Constants.DOUBLE_MAX_POS) / digit) + 0.5);
|
|
||||||
|
|
||||||
while (mantissa >= 10000000000000000L) {
|
|
||||||
mantissa /= 10;
|
|
||||||
exp--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove trailing zeros
|
// Remove trailing zeros
|
||||||
digits = Constants.DOUBLE_DECIMAL_PRECISION;
|
int digits = DoubleAnalyzer.DECIMAL_PRECISION;
|
||||||
int zeros = trailingDecimalZeros(mantissa);
|
int zeros = trailingDecimalZeros(mantissa);
|
||||||
if (zeros > 0) {
|
if (zeros > 0) {
|
||||||
digits -= zeros;
|
digits -= zeros;
|
||||||
|
@ -482,7 +442,7 @@ class TAbstractStringBuilder extends TObject implements TSerializable, TCharSequ
|
||||||
if (negative) {
|
if (negative) {
|
||||||
buffer[target++] = '-';
|
buffer[target++] = '-';
|
||||||
}
|
}
|
||||||
long pos = Constants.DOUBLE_MAX_POS;
|
long pos = DoubleAnalyzer.DOUBLE_MAX_POS;
|
||||||
for (int i = 0; i < digits; ++i) {
|
for (int i = 0; i < digits; ++i) {
|
||||||
int intDigit;
|
int intDigit;
|
||||||
if (pos > 0) {
|
if (pos > 0) {
|
||||||
|
|
|
@ -243,9 +243,11 @@ public class TDouble extends TNumber implements TComparable<TDouble> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@InjectedBy(DoubleGenerator.class)
|
@InjectedBy(DoubleGenerator.class)
|
||||||
|
@Import(name = "teavm_reinterpretDoubleToLong")
|
||||||
public static native long doubleToLongBits(double value);
|
public static native long doubleToLongBits(double value);
|
||||||
|
|
||||||
@InjectedBy(DoubleGenerator.class)
|
@InjectedBy(DoubleGenerator.class)
|
||||||
|
@Import(name = "teavm_reinterpretLongToDouble")
|
||||||
public static native double longBitsToDouble(long bits);
|
public static native double longBitsToDouble(long bits);
|
||||||
|
|
||||||
public static TString toHexString(double d) {
|
public static TString toHexString(double d) {
|
||||||
|
|
|
@ -247,9 +247,11 @@ public class TFloat extends TNumber implements TComparable<TFloat> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@JSBody(params = "value", script = "return $rt_floatToIntBits(value);")
|
@JSBody(params = "value", script = "return $rt_floatToIntBits(value);")
|
||||||
|
@Import(name = "teavm_reinterpretFloatToInt")
|
||||||
public static native int floatToIntBits(float value);
|
public static native int floatToIntBits(float value);
|
||||||
|
|
||||||
@JSBody(params = "bits", script = "return $rt_intBitsToFloat(bits);")
|
@JSBody(params = "bits", script = "return $rt_intBitsToFloat(bits);")
|
||||||
|
@Import(name = "teavm_reinterpretIntToFloat")
|
||||||
public static native float intBitsToFloat(int bits);
|
public static native float intBitsToFloat(int bits);
|
||||||
|
|
||||||
public static TString toHexString(float f) {
|
public static TString toHexString(float f) {
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
package org.teavm.classlib.java.lang;
|
package org.teavm.classlib.java.lang;
|
||||||
|
|
||||||
import static org.teavm.classlib.impl.IntegerUtil.toUnsignedLogRadixString;
|
import static org.teavm.classlib.impl.IntegerUtil.toUnsignedLogRadixString;
|
||||||
|
import org.teavm.backend.javascript.spi.GeneratedBy;
|
||||||
|
|
||||||
public class TLong extends TNumber implements TComparable<TLong> {
|
public class TLong extends TNumber implements TComparable<TLong> {
|
||||||
public static final long MIN_VALUE = -0x8000000000000000L;
|
public static final long MIN_VALUE = -0x8000000000000000L;
|
||||||
|
@ -348,4 +349,10 @@ public class TLong extends TNumber implements TComparable<TLong> {
|
||||||
public static int signum(long i) {
|
public static int signum(long i) {
|
||||||
return (int) ((i >> 63) | (-i >>> 63));
|
return (int) ((i >> 63) | (-i >>> 63));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GeneratedBy(LongNativeGenerator.class)
|
||||||
|
public static native long divideUnsigned(long dividend, long divisor);
|
||||||
|
|
||||||
|
@GeneratedBy(LongNativeGenerator.class)
|
||||||
|
public static native long remainderUnsigned(long dividend, long divisor);
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ public final class TSystem extends TObject {
|
||||||
|
|
||||||
public static TPrintStream out() {
|
public static TPrintStream out() {
|
||||||
if (outCache == null) {
|
if (outCache == null) {
|
||||||
new TPrintStream(new TConsoleOutputStreamStdout(), false);
|
outCache = new TPrintStream(new TConsoleOutputStreamStdout(), false);
|
||||||
}
|
}
|
||||||
return outCache;
|
return outCache;
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,7 @@ import org.teavm.backend.c.intrinsic.FunctionIntrinsic;
|
||||||
import org.teavm.backend.c.intrinsic.GCIntrinsic;
|
import org.teavm.backend.c.intrinsic.GCIntrinsic;
|
||||||
import org.teavm.backend.c.intrinsic.Intrinsic;
|
import org.teavm.backend.c.intrinsic.Intrinsic;
|
||||||
import org.teavm.backend.c.intrinsic.IntrinsicFactory;
|
import org.teavm.backend.c.intrinsic.IntrinsicFactory;
|
||||||
|
import org.teavm.backend.c.intrinsic.LongIntrinsic;
|
||||||
import org.teavm.backend.c.intrinsic.MutatorIntrinsic;
|
import org.teavm.backend.c.intrinsic.MutatorIntrinsic;
|
||||||
import org.teavm.backend.c.intrinsic.PlatformClassIntrinsic;
|
import org.teavm.backend.c.intrinsic.PlatformClassIntrinsic;
|
||||||
import org.teavm.backend.c.intrinsic.PlatformClassMetadataIntrinsic;
|
import org.teavm.backend.c.intrinsic.PlatformClassMetadataIntrinsic;
|
||||||
|
@ -234,6 +235,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||||
intrinsics.add(new ExceptionHandlingIntrinsic());
|
intrinsics.add(new ExceptionHandlingIntrinsic());
|
||||||
intrinsics.add(new FunctionIntrinsic(characteristics, exportDependencyListener.getResolvedMethods()));
|
intrinsics.add(new FunctionIntrinsic(characteristics, exportDependencyListener.getResolvedMethods()));
|
||||||
intrinsics.add(new RuntimeClassIntrinsic());
|
intrinsics.add(new RuntimeClassIntrinsic());
|
||||||
|
intrinsics.add(new LongIntrinsic());
|
||||||
|
|
||||||
List<Generator> generators = new ArrayList<>();
|
List<Generator> generators = new ArrayList<>();
|
||||||
generators.add(new ArrayGenerator());
|
generators.add(new ArrayGenerator());
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.teavm.backend.c.intrinsic;
|
||||||
|
|
||||||
|
import org.teavm.ast.InvocationExpr;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
|
public class LongIntrinsic implements Intrinsic {
|
||||||
|
@Override
|
||||||
|
public boolean canHandle(MethodReference method) {
|
||||||
|
if (!method.getClassName().equals(Long.class.getName())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
switch (method.getName()) {
|
||||||
|
case "divideUnsigned":
|
||||||
|
case "remainderUnsigned":
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(IntrinsicContext context, InvocationExpr invocation) {
|
||||||
|
switch (invocation.getMethod().getName()) {
|
||||||
|
case "divideUnsigned":
|
||||||
|
writeBinary(context, invocation, "/");
|
||||||
|
break;
|
||||||
|
case "remainderUnsigned":
|
||||||
|
writeBinary(context, invocation, "%");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeBinary(IntrinsicContext context, InvocationExpr invocation, String operation) {
|
||||||
|
context.writer().print("((int64_t) ((uint64_t) ");
|
||||||
|
context.emit(invocation.getArguments().get(0));
|
||||||
|
context.writer().print(" " + operation + " (uint64_t) ");
|
||||||
|
context.emit(invocation.getArguments().get(1));
|
||||||
|
context.writer().print("))");
|
||||||
|
}
|
||||||
|
}
|
|
@ -1006,6 +1006,11 @@ public class Renderer implements RenderingManager {
|
||||||
public String typeToClassString(ValueType type) {
|
public String typeToClassString(ValueType type) {
|
||||||
return context.typeToClsString(type);
|
return context.typeToClsString(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void useLongLibrary() {
|
||||||
|
longLibraryUsed = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void appendMonitor(StatementRenderer statementRenderer, MethodNode methodNode) throws IOException {
|
private void appendMonitor(StatementRenderer statementRenderer, MethodNode methodNode) throws IOException {
|
||||||
|
|
|
@ -43,4 +43,6 @@ public interface GeneratorContext extends ServiceRepository {
|
||||||
Diagnostics getDiagnostics();
|
Diagnostics getDiagnostics();
|
||||||
|
|
||||||
String typeToClassString(ValueType type);
|
String typeToClassString(ValueType type);
|
||||||
|
|
||||||
|
void useLongLibrary();
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,7 @@ import org.teavm.backend.wasm.intrinsics.ExceptionHandlingIntrinsic;
|
||||||
import org.teavm.backend.wasm.intrinsics.FloatIntrinsic;
|
import org.teavm.backend.wasm.intrinsics.FloatIntrinsic;
|
||||||
import org.teavm.backend.wasm.intrinsics.FunctionIntrinsic;
|
import org.teavm.backend.wasm.intrinsics.FunctionIntrinsic;
|
||||||
import org.teavm.backend.wasm.intrinsics.GCIntrinsic;
|
import org.teavm.backend.wasm.intrinsics.GCIntrinsic;
|
||||||
|
import org.teavm.backend.wasm.intrinsics.LongIntrinsic;
|
||||||
import org.teavm.backend.wasm.intrinsics.MutatorIntrinsic;
|
import org.teavm.backend.wasm.intrinsics.MutatorIntrinsic;
|
||||||
import org.teavm.backend.wasm.intrinsics.ObjectIntrinsic;
|
import org.teavm.backend.wasm.intrinsics.ObjectIntrinsic;
|
||||||
import org.teavm.backend.wasm.intrinsics.PlatformClassIntrinsic;
|
import org.teavm.backend.wasm.intrinsics.PlatformClassIntrinsic;
|
||||||
|
@ -343,6 +344,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
||||||
context.addIntrinsic(new RuntimeClassIntrinsic());
|
context.addIntrinsic(new RuntimeClassIntrinsic());
|
||||||
context.addIntrinsic(new FloatIntrinsic());
|
context.addIntrinsic(new FloatIntrinsic());
|
||||||
context.addIntrinsic(new DoubleIntrinsic());
|
context.addIntrinsic(new DoubleIntrinsic());
|
||||||
|
context.addIntrinsic(new LongIntrinsic());
|
||||||
context.addIntrinsic(new ObjectIntrinsic());
|
context.addIntrinsic(new ObjectIntrinsic());
|
||||||
context.addGenerator(new ArrayGenerator());
|
context.addGenerator(new ArrayGenerator());
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.teavm.backend.wasm.intrinsics;
|
||||||
|
|
||||||
|
import org.teavm.ast.InvocationExpr;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmIntBinary;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmIntType;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
|
public class LongIntrinsic implements WasmIntrinsic {
|
||||||
|
@Override
|
||||||
|
public boolean isApplicable(MethodReference methodReference) {
|
||||||
|
if (!methodReference.getClassName().equals(Long.class.getName())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (methodReference.getName()) {
|
||||||
|
case "divideUnsigned":
|
||||||
|
case "remainderUnsigned":
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) {
|
||||||
|
switch (invocation.getMethod().getName()) {
|
||||||
|
case "divideUnsigned":
|
||||||
|
return new WasmIntBinary(WasmIntType.INT64, WasmIntBinaryOperation.DIV_UNSIGNED,
|
||||||
|
manager.generate(invocation.getArguments().get(0)),
|
||||||
|
manager.generate(invocation.getArguments().get(1)));
|
||||||
|
case "remainderUnsigned":
|
||||||
|
return new WasmIntBinary(WasmIntType.INT64, WasmIntBinaryOperation.REM_UNSIGNED,
|
||||||
|
manager.generate(invocation.getArguments().get(0)),
|
||||||
|
manager.generate(invocation.getArguments().get(1)));
|
||||||
|
default:
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -273,4 +273,25 @@ static int32_t teavm_timeZoneOffset() {
|
||||||
}
|
}
|
||||||
|
|
||||||
static char* teavm_stringToC(void*);
|
static char* teavm_stringToC(void*);
|
||||||
static inline void teavm_free(void*);
|
static inline void teavm_free(void*);
|
||||||
|
|
||||||
|
static inline int64_t teavm_reinterpretDoubleToLong(double v) {
|
||||||
|
union { int64_t longValue; double doubleValue; } conv;
|
||||||
|
conv.doubleValue = v;
|
||||||
|
return conv.longValue;
|
||||||
|
}
|
||||||
|
static inline double teavm_reinterpretLongToDouble(int64_t v) {
|
||||||
|
union { int64_t longValue; double doubleValue; } conv;
|
||||||
|
conv.longValue = v;
|
||||||
|
return conv.doubleValue;
|
||||||
|
}
|
||||||
|
static inline int32_t teavm_reinterpretFloatToInt(float v) {
|
||||||
|
union { int32_t intValue; float floatValue; } conv;
|
||||||
|
conv.floatValue = v;
|
||||||
|
return conv.intValue;
|
||||||
|
}
|
||||||
|
static inline float teavm_reinterpretIntToFloat(int32_t v) {
|
||||||
|
union { int32_t intValue; float floatValue; } conv;
|
||||||
|
conv.intValue = v;
|
||||||
|
return conv.floatValue;
|
||||||
|
}
|
|
@ -197,12 +197,24 @@ function Long_div(a, b) {
|
||||||
}
|
}
|
||||||
return Long_divRem(a, b)[0];
|
return Long_divRem(a, b)[0];
|
||||||
}
|
}
|
||||||
|
function Long_udiv(a, b) {
|
||||||
|
if (a.hi >= 0 && a.hi < Long_MAX_NORMAL && b.hi >= 0 && b.hi < Long_MAX_NORMAL) {
|
||||||
|
return Long_fromNumber(Long_toNumber(a) / Long_toNumber(b));
|
||||||
|
}
|
||||||
|
return Long_udivRem(a, b)[0];
|
||||||
|
}
|
||||||
function Long_rem(a, b) {
|
function Long_rem(a, b) {
|
||||||
if (Math.abs(a.hi) < Long_MAX_NORMAL && Math.abs(b.hi) < Long_MAX_NORMAL) {
|
if (Math.abs(a.hi) < Long_MAX_NORMAL && Math.abs(b.hi) < Long_MAX_NORMAL) {
|
||||||
return Long_fromNumber(Long_toNumber(a) % Long_toNumber(b));
|
return Long_fromNumber(Long_toNumber(a) % Long_toNumber(b));
|
||||||
}
|
}
|
||||||
return Long_divRem(a, b)[1];
|
return Long_divRem(a, b)[1];
|
||||||
}
|
}
|
||||||
|
function Long_urem(a, b) {
|
||||||
|
if (a.hi >= 0 && a.hi < Long_MAX_NORMAL && b.hi >= 0 && b.hi < Long_MAX_NORMAL) {
|
||||||
|
return Long_fromNumber(Long_toNumber(a) / Long_toNumber(b));
|
||||||
|
}
|
||||||
|
return Long_udivRem(a, b)[1];
|
||||||
|
}
|
||||||
function Long_divRem(a, b) {
|
function Long_divRem(a, b) {
|
||||||
if (b.lo === 0 && b.hi === 0) {
|
if (b.lo === 0 && b.hi === 0) {
|
||||||
throw new Error("Division by zero");
|
throw new Error("Division by zero");
|
||||||
|
@ -221,6 +233,17 @@ function Long_divRem(a, b) {
|
||||||
q = new Long(q.lo, q.hi);
|
q = new Long(q.lo, q.hi);
|
||||||
return positive ? [q, a] : [Long_neg(q), Long_neg(a)];
|
return positive ? [q, a] : [Long_neg(q), Long_neg(a)];
|
||||||
}
|
}
|
||||||
|
function Long_udivRem(a, b) {
|
||||||
|
if (b.lo === 0 && b.hi === 0) {
|
||||||
|
throw new Error("Division by zero");
|
||||||
|
}
|
||||||
|
a = new LongInt(a.lo, a.hi, 0);
|
||||||
|
b = new LongInt(b.lo, b.hi, 0);
|
||||||
|
var q = LongInt_div(a, b);
|
||||||
|
a = new Long(a.lo, a.hi);
|
||||||
|
q = new Long(q.lo, q.hi);
|
||||||
|
return [q, a];
|
||||||
|
}
|
||||||
function Long_shiftLeft16(a) {
|
function Long_shiftLeft16(a) {
|
||||||
return new Long(a.lo << 16, (a.lo >>> 16) | (a.hi << 16));
|
return new Long(a.lo << 16, (a.lo >>> 16) | (a.hi << 16));
|
||||||
}
|
}
|
||||||
|
|
|
@ -279,8 +279,8 @@ public class StringBuilderTest {
|
||||||
@Test
|
@Test
|
||||||
public void minDoubleAppended() {
|
public void minDoubleAppended() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(2.2250738585072E-308);
|
sb.append(3E-308);
|
||||||
assertEquals("2.2250738585072E-308", sb.toString());
|
assertEquals("3.0E-308", sb.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
Loading…
Reference in New Issue
Block a user