Use JS typed arrays to reinterpret floats and ints.

Also, drop IE9 support
This commit is contained in:
Alexey Andreev 2018-11-23 14:44:18 +03:00
parent b9ec21a667
commit cf9090e0fa
5 changed files with 90 additions and 191 deletions

View File

@ -0,0 +1,30 @@
/*
* 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.java.lang;
import java.io.IOException;
import org.teavm.backend.javascript.spi.Injector;
import org.teavm.backend.javascript.spi.InjectorContext;
import org.teavm.model.MethodReference;
public class DoubleGenerator implements Injector {
@Override
public void generate(InjectorContext context, MethodReference methodRef) throws IOException {
context.getWriter().append("$rt_").append(methodRef.getName()).append("(");
context.writeExpr(context.getArgument(0));
context.getWriter().append(")");
}
}

View File

@ -415,9 +415,13 @@ class TAbstractStringBuilder extends TObject implements TSerializable, TCharSequ
exp = 0; exp = 0;
double digit = 1; double digit = 1;
for (int i = negDoublePowersOfTen.length - 1; i >= 0; --i) { for (int i = negDoublePowersOfTen.length - 1; i >= 0; --i) {
if ((exp | bit) <= DOUBLE_MAX_EXPONENT && negDoublePowersOfTen[i] * digit * 10 > value) { if ((exp | bit) <= 324 && negDoublePowersOfTen[i] * digit * 10 > value) {
digit *= negDoublePowersOfTen[i];
exp |= bit; exp |= bit;
if (exp == 324) {
value /= negDoublePowersOfTen[i];
} else {
digit *= negDoublePowersOfTen[i];
}
} }
bit >>= 1; bit >>= 1;
} }

View File

@ -15,6 +15,7 @@
*/ */
package org.teavm.classlib.java.lang; package org.teavm.classlib.java.lang;
import org.teavm.backend.javascript.spi.InjectedBy;
import org.teavm.interop.Import; import org.teavm.interop.Import;
import org.teavm.jso.JSBody; import org.teavm.jso.JSBody;
@ -241,52 +242,11 @@ public class TDouble extends TNumber implements TComparable<TDouble> {
return doubleToLongBits(value); return doubleToLongBits(value);
} }
public static long doubleToLongBits(double value) { @InjectedBy(DoubleGenerator.class)
if (value == POSITIVE_INFINITY) { public static native long doubleToLongBits(double value);
return 0x7FF0000000000000L;
} else if (value == NEGATIVE_INFINITY) {
return 0xFFF0000000000000L;
} else if (isNaN(value)) {
return 0x7FF8000000000000L;
}
double abs = TMath.abs(value);
int exp = TMath.getExponent(abs);
int negExp = -exp + 52;
if (exp < -1022) {
exp = -1023;
negExp = 1022 + 52;
}
double doubleMantissa;
if (negExp <= 1022) {
doubleMantissa = abs * binaryExponent(negExp);
} else {
doubleMantissa = abs * 0x1p1022 * binaryExponent(negExp - 1022);
}
long mantissa = (long) (doubleMantissa + 0.5) & 0xFFFFFFFFFFFFFL;
return mantissa | ((exp + 1023L) << 52) | (value < 0 || 1 / value == NEGATIVE_INFINITY ? (1L << 63) : 0);
}
public static double longBitsToDouble(long bits) { @InjectedBy(DoubleGenerator.class)
if ((bits & 0x7FF0000000000000L) == 0x7FF0000000000000L) { public static native double longBitsToDouble(long bits);
if (bits == 0x7FF0000000000000L) {
return POSITIVE_INFINITY;
} else if (bits == 0xFFF0000000000000L) {
return NEGATIVE_INFINITY;
} else {
return NaN;
}
}
boolean negative = (bits & (1L << 63)) != 0;
int rawExp = (int) ((bits >> 52) & 0x7FFL);
long mantissa = bits & 0xFFFFFFFFFFFFFL;
if (rawExp == 0) {
mantissa <<= 1;
} else {
mantissa |= 1L << 52;
}
double value = mantissa * binaryExponent(rawExp - 1023 - 52);
return !negative ? value : -value;
}
public static TString toHexString(double d) { public static TString toHexString(double d) {
if (isNaN(d)) { if (isNaN(d)) {
@ -351,29 +311,4 @@ public class TDouble extends TNumber implements TComparable<TDouble> {
return new TString(buffer, 0, sz); return new TString(buffer, 0, sz);
} }
private static double binaryExponent(int n) {
double result = 1;
if (n >= 0) {
double d = 2;
while (n != 0) {
if (n % 2 != 0) {
result *= d;
}
n /= 2;
d *= d;
}
} else {
n = -n;
double d = 0.5;
while (n != 0) {
if (n % 2 != 0) {
result *= d;
}
n /= 2;
d *= d;
}
}
return result;
}
} }

View File

@ -246,77 +246,11 @@ public class TFloat extends TNumber implements TComparable<TFloat> {
return floatToIntBits(value); return floatToIntBits(value);
} }
public static int floatToIntBits(float value) { @JSBody(params = "value", script = "return $rt_floatToIntBits(value);")
if (value == POSITIVE_INFINITY) { public static native int floatToIntBits(float value);
return 0x7F800000;
} else if (value == NEGATIVE_INFINITY) {
return 0xFF800000;
} else if (isNaN(value)) {
return 0x7FC00000;
}
float abs = TMath.abs(value);
int exp = TMath.getExponent(abs);
int negExp = -exp + 23;
if (exp < -126) {
exp = -127;
negExp = 126 + 23;
}
float doubleMantissa;
if (negExp <= 126) {
doubleMantissa = abs * binaryExponent(negExp);
} else {
doubleMantissa = abs * 0x1p126f * binaryExponent(negExp - 126);
}
int mantissa = (int) (doubleMantissa + 0.5f) & 0x7FFFFF;
return mantissa | ((exp + 127) << 23) | (value < 0 || 1 / value == NEGATIVE_INFINITY ? (1 << 31) : 0);
}
public static float intBitsToFloat(int bits) { @JSBody(params = "bits", script = "return $rt_intBitsToFloat(bits);")
if ((bits & 0x7F800000) == 0x7F800000) { public static native float intBitsToFloat(int bits);
if (bits == 0x7F800000) {
return POSITIVE_INFINITY;
} else if (bits == 0xFF800000) {
return NEGATIVE_INFINITY;
} else {
return NaN;
}
}
boolean negative = (bits & (1 << 31)) != 0;
int rawExp = (bits >> 23) & 0xFF;
int mantissa = bits & 0x7FFFFF;
if (rawExp == 0) {
mantissa <<= 1;
} else {
mantissa |= 1L << 23;
}
float value = mantissa * binaryExponent(rawExp - 127 - 23);
return !negative ? value : -value;
}
private static float binaryExponent(int n) {
float result = 1;
if (n >= 0) {
float d = 2;
while (n != 0) {
if (n % 2 != 0) {
result *= d;
}
n /= 2;
d *= d;
}
} else {
n = -n;
float d = 0.5f;
while (n != 0) {
if (n % 2 != 0) {
result *= d;
}
n /= 2;
d *= d;
}
}
return result;
}
public static TString toHexString(float f) { public static TString toHexString(float f) {
if (isNaN(f)) { if (isNaN(f)) {

View File

@ -67,56 +67,31 @@ function $rt_createLongArray(sz) {
} }
return arr; return arr;
} }
var $rt_createNumericArray; function $rt_createNumericArray(cls, nativeArray) {
var $rt_createCharArray;
var $rt_createByteArray;
var $rt_createShortArray;
var $rt_createIntArray;
var $rt_createBooleanArray;
var $rt_createFloatArray;
var $rt_createDoubleArray;
if (typeof 'ArrayBuffer' !== 'undefined') {
$rt_createNumericArray = function(cls, nativeArray) {
return new ($rt_arraycls(cls))(nativeArray); return new ($rt_arraycls(cls))(nativeArray);
}; }
$rt_createCharArray = function(sz) { function $rt_createCharArray(sz) {
return $rt_createNumericArray($rt_charcls(), new Uint16Array(sz)); return $rt_createNumericArray($rt_charcls(), new Uint16Array(sz));
}; }
$rt_createByteArray = function(sz) { function $rt_createByteArray(sz) {
return $rt_createNumericArray($rt_bytecls(), new Int8Array(sz)); return $rt_createNumericArray($rt_bytecls(), new Int8Array(sz));
}; }
$rt_createShortArray = function(sz) { function $rt_createShortArray(sz) {
return $rt_createNumericArray($rt_shortcls(), new Int16Array(sz)); return $rt_createNumericArray($rt_shortcls(), new Int16Array(sz));
}; }
$rt_createIntArray = function(sz) { function $rt_createIntArray(sz) {
return $rt_createNumericArray($rt_intcls(), new Int32Array(sz)); return $rt_createNumericArray($rt_intcls(), new Int32Array(sz));
}; }
$rt_createBooleanArray = function(sz) { function $rt_createBooleanArray(sz) {
return $rt_createNumericArray($rt_booleancls(), new Int8Array(sz)); return $rt_createNumericArray($rt_booleancls(), new Int8Array(sz));
}; }
$rt_createFloatArray = function(sz) { function $rt_createFloatArray(sz) {
return $rt_createNumericArray($rt_floatcls(), new Float32Array(sz)); return $rt_createNumericArray($rt_floatcls(), new Float32Array(sz));
}; }
$rt_createDoubleArray = function(sz) { function $rt_createDoubleArray(sz) {
return $rt_createNumericArray($rt_doublecls(), new Float64Array(sz)); return $rt_createNumericArray($rt_doublecls(), new Float64Array(sz));
};
} else {
$rt_createNumericArray = function(cls, sz) {
var data = new Array(sz);
var arr = new ($rt_arraycls(cls))(data);
for (var i = 0; i < sz; i = (i + 1) | 0) {
data[i] = 0;
}
return arr;
};
$rt_createByteArray = function(sz) { return $rt_createNumericArray($rt_bytecls(), sz); };
$rt_createShortArray = function(sz) { return $rt_createNumericArray($rt_shortcls(), sz); };
$rt_createIntArray = function(sz) { return $rt_createNumericArray($rt_intcls(), sz); };
$rt_createBooleanArray = function(sz) { return $rt_createNumericArray($rt_booleancls(), sz); };
$rt_createFloatArray = function(sz) { return $rt_createNumericArray($rt_floatcls(), sz); };
$rt_createDoubleArray = function(sz) { return $rt_createNumericArray($rt_doublecls(), sz); };
$rt_createCharArray = function(sz) { return $rt_createNumericArray($rt_charcls(), sz); }
} }
function $rt_arraycls(cls) { function $rt_arraycls(cls) {
var result = cls.$array; var result = cls.$array;
if (result === null) { if (result === null) {
@ -615,6 +590,27 @@ function $rt_nativeThread() {
function $rt_invalidPointer() { function $rt_invalidPointer() {
throw new Error("Invalid recorded state"); throw new Error("Invalid recorded state");
} }
var $rt_numberConversionView = new DataView(new ArrayBuffer(8));
function $rt_doubleToLongBits(n) {
$rt_numberConversionView.setFloat64(0, n, true);
return new Long($rt_numberConversionView.getInt32(0, true), $rt_numberConversionView.getInt32(4, true));
}
function $rt_longBitsToDouble(n) {
$rt_numberConversionView.setInt32(0, n.lo, true);
$rt_numberConversionView.setInt32(4, n.hi, true);
return $rt_numberConversionView.getFloat64(0, true);
}
function $rt_floatToIntBits(n) {
$rt_numberConversionView.setFloat32(0, n);
return $rt_numberConversionView.getInt32(0);
}
function $rt_intBitsToFloat(n) {
$rt_numberConversionView.setInt32(0, n);
return $rt_numberConversionView.getFloat32(0);
}
function $dbg_class(obj) { function $dbg_class(obj) {
var cls = obj.constructor; var cls = obj.constructor;
var arrayDegree = 0; var arrayDegree = 0;