mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
classlib: improve precision of float/double parsing and formatting
This commit is contained in:
parent
e6c71fa106
commit
e2ee9f1dbb
|
@ -64,3 +64,4 @@ tasks {
|
|||
teavmPublish {
|
||||
artifactId = "teavm-classlib"
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright 2023 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.math.BigInteger;
|
||||
|
||||
public final class DoubleAnalyzerGenerator {
|
||||
private DoubleAnalyzerGenerator() {
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
var mantissaList = new long[660];
|
||||
var expList = new long[660];
|
||||
|
||||
var dec = BigInteger.valueOf(1000000000000000000L).shiftLeft(1024 + 64);
|
||||
for (var i = 0; i < 330; ++i) {
|
||||
var shift = dec.bitLength() - 64;
|
||||
mantissaList[330 + i] = dec.shiftRight(shift).longValue();
|
||||
dec = dec.divide(BigInteger.valueOf(10));
|
||||
var exp = 1024 + 64 - shift;
|
||||
expList[330 + i] = 1023 + exp;
|
||||
}
|
||||
|
||||
dec = BigInteger.valueOf(1000000000000000000L);
|
||||
for (var i = 1; i <= 330; ++i) {
|
||||
dec = dec.multiply(BigInteger.valueOf(10));
|
||||
var shift = dec.bitLength() - 64;
|
||||
mantissaList[330 - i] = dec.shiftRight(shift).longValue();
|
||||
expList[330 - i] = 1023 - shift;
|
||||
}
|
||||
|
||||
System.out.println("[mantissa]");
|
||||
for (var value : mantissaList) {
|
||||
System.out.println(value + "L,");
|
||||
}
|
||||
System.out.println();
|
||||
|
||||
System.out.println("[exponent]");
|
||||
for (var value : expList) {
|
||||
System.out.println(value + ",");
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright 2023 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.math.BigInteger;
|
||||
|
||||
public final class DoubleSynthesizerGenerator {
|
||||
private DoubleSynthesizerGenerator() {
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
var mantissaList = new long[660];
|
||||
var expList = new long[660];
|
||||
var shift = 122;
|
||||
|
||||
var exp = 0;
|
||||
var dec = BigInteger.valueOf(1000000000000000000L);
|
||||
for (var i = 0; i < 330; ++i) {
|
||||
while (BigInteger.ONE.shiftLeft(shift + exp + 1).divide(dec).bitLength() <= 64) {
|
||||
++exp;
|
||||
}
|
||||
mantissaList[330 + i] = BigInteger.ONE.shiftLeft(shift + exp).divide(dec).longValue();
|
||||
dec = dec.multiply(BigInteger.valueOf(10));
|
||||
expList[330 + i] = 1023 - exp;
|
||||
}
|
||||
|
||||
exp = 1;
|
||||
dec = BigInteger.valueOf(1000000000000000000L).multiply(BigInteger.ONE.shiftLeft(1024));
|
||||
var q = BigInteger.valueOf(10L);
|
||||
for (var i = 1; i <= 330; ++i) {
|
||||
while (BigInteger.ONE.shiftLeft(shift + 1024 - exp).multiply(q).divide(dec).bitLength() > 64) {
|
||||
++exp;
|
||||
}
|
||||
mantissaList[330 - i] = BigInteger.ONE.shiftLeft(shift + 1024 - exp).multiply(q).divide(dec).longValue();
|
||||
q = q.multiply(BigInteger.valueOf(10));
|
||||
expList[330 - i] = 1023 + exp;
|
||||
}
|
||||
|
||||
System.out.println("[mantissa]");
|
||||
for (var value : mantissaList) {
|
||||
System.out.println(value + "L,");
|
||||
}
|
||||
System.out.println();
|
||||
|
||||
System.out.println("[exponent]");
|
||||
for (var value : expList) {
|
||||
System.out.println(value + ",");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,58 +20,11 @@ import java.util.Arrays;
|
|||
public final class FloatAnalyzer {
|
||||
public static final int PRECISION = 9;
|
||||
public static final int MAX_POS = 100000000;
|
||||
|
||||
private static final int MAX_ABS_DEC_EXP = 50;
|
||||
private static final int[] mantissa10Table = new int[MAX_ABS_DEC_EXP * 2];
|
||||
private static final int[] exp10Table = new int[MAX_ABS_DEC_EXP * 2];
|
||||
static final int MAX_ABS_DEC_EXP = 50;
|
||||
|
||||
private FloatAnalyzer() {
|
||||
}
|
||||
|
||||
static {
|
||||
int decMantissaOne = 2000000000;
|
||||
|
||||
int mantissa = decMantissaOne;
|
||||
int exponent = 127;
|
||||
|
||||
for (int i = 0; i < MAX_ABS_DEC_EXP; ++i) {
|
||||
mantissa10Table[i + MAX_ABS_DEC_EXP] = Integer.divideUnsigned(mantissa, 20);
|
||||
exp10Table[i + MAX_ABS_DEC_EXP] = exponent;
|
||||
|
||||
mantissa = Integer.divideUnsigned(mantissa, 10);
|
||||
int remainder = Integer.remainderUnsigned(mantissa, 10);
|
||||
while (mantissa <= decMantissaOne && (mantissa & (1 << 31)) == 0) {
|
||||
mantissa <<= 1;
|
||||
exponent++;
|
||||
remainder <<= 1;
|
||||
}
|
||||
mantissa += remainder / 10;
|
||||
}
|
||||
|
||||
int maxMantissa = Integer.MAX_VALUE / 10;
|
||||
mantissa = decMantissaOne;
|
||||
exponent = 127;
|
||||
for (int i = 0; i < MAX_ABS_DEC_EXP; ++i) {
|
||||
int 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] = Integer.divideUnsigned(mantissa, 20);
|
||||
exp10Table[MAX_ABS_DEC_EXP - i - 1] = exponent;
|
||||
}
|
||||
}
|
||||
|
||||
public static void analyze(float d, Result result) {
|
||||
int bits = Float.floatToIntBits(d);
|
||||
result.sign = (bits & (1 << 31)) != 0;
|
||||
|
@ -83,13 +36,11 @@ public final class FloatAnalyzer {
|
|||
return;
|
||||
}
|
||||
|
||||
int errorShift = 0;
|
||||
if (exponent == 0) {
|
||||
mantissa <<= 1;
|
||||
while ((mantissa & (1L << 23)) == 0) {
|
||||
mantissa <<= 1;
|
||||
exponent--;
|
||||
++errorShift;
|
||||
}
|
||||
} else {
|
||||
mantissa |= 1 << 23;
|
||||
|
@ -97,37 +48,36 @@ public final class FloatAnalyzer {
|
|||
|
||||
int decExponent = Arrays.binarySearch(exp10Table, exponent);
|
||||
if (decExponent < 0) {
|
||||
decExponent = -decExponent - 2;
|
||||
decExponent = -decExponent;
|
||||
}
|
||||
int binExponentCorrection = exponent - exp10Table[decExponent];
|
||||
int binExponentCorrection = exponent - exp10Table[decExponent + 1];
|
||||
int mantissaShift = 9 + binExponentCorrection;
|
||||
|
||||
int decMantissa = (int) (((long) mantissa * mantissa10Table[decExponent]) >>> (32 - mantissaShift));
|
||||
int decMantissa = mulAndShiftRight(mantissa, mantissa10Table[decExponent + 1], mantissaShift);
|
||||
if (decMantissa >= 1000000000) {
|
||||
++decExponent;
|
||||
binExponentCorrection = exponent - exp10Table[decExponent];
|
||||
binExponentCorrection = exponent - exp10Table[decExponent + 1];
|
||||
mantissaShift = 9 + binExponentCorrection;
|
||||
decMantissa = (int) (((long) mantissa * mantissa10Table[decExponent]) >>> (32 - mantissaShift));
|
||||
decMantissa = mulAndShiftRight(mantissa, mantissa10Table[decExponent + 1], mantissaShift);
|
||||
} else if (decMantissa < 100000000) {
|
||||
--decExponent;
|
||||
binExponentCorrection = exponent - exp10Table[decExponent + 1];
|
||||
mantissaShift = 9 + binExponentCorrection;
|
||||
decMantissa = mulAndShiftRight(mantissa, mantissa10Table[decExponent + 1], mantissaShift);
|
||||
}
|
||||
var decMantissaHi = mulAndShiftRight((mantissa << 1) + 1, mantissa10Table[decExponent + 1],
|
||||
mantissaShift - 1);
|
||||
var decMantissaLow = mulAndShiftRight((mantissa << 1) - 1, mantissa10Table[decExponent + 1],
|
||||
mantissaShift - 1);
|
||||
|
||||
errorShift = 31 - mantissaShift - errorShift;
|
||||
int error = errorShift >= 0
|
||||
? mantissa10Table[decExponent] >>> errorShift
|
||||
: mantissa10Table[decExponent] << (-errorShift);
|
||||
int upError = (error + 1) >> 1;
|
||||
int downError = error >> 1;
|
||||
if (mantissa == (1 << 22)) {
|
||||
downError >>= 2;
|
||||
}
|
||||
|
||||
int lowerPos = findLowerDistanceToZero(decMantissa, downError);
|
||||
int upperPos = findUpperDistanceToZero(decMantissa, upError);
|
||||
var lowerPos = findLowerDistance(decMantissa, decMantissaLow);
|
||||
var upperPos = findUpperDistance(decMantissa, decMantissaHi);
|
||||
if (lowerPos > upperPos) {
|
||||
decMantissa = (decMantissa / lowerPos) * lowerPos;
|
||||
} else if (lowerPos < upperPos) {
|
||||
decMantissa = (decMantissa / upperPos) * upperPos + upperPos;
|
||||
} else {
|
||||
decMantissa = ((decMantissa + upperPos / 2) / upperPos) * upperPos;
|
||||
decMantissa = ((decMantissa + (upperPos / 2)) / upperPos) * upperPos;
|
||||
}
|
||||
|
||||
if (decMantissa >= 1000000000) {
|
||||
|
@ -142,33 +92,242 @@ public final class FloatAnalyzer {
|
|||
result.exponent = decExponent - MAX_ABS_DEC_EXP;
|
||||
}
|
||||
|
||||
private static int findLowerDistanceToZero(int mantissa, int error) {
|
||||
int pos = 10;
|
||||
while (pos <= error) {
|
||||
private static int findLowerDistance(int mantissa, int lower) {
|
||||
int pos = 1;
|
||||
while (mantissa / (pos * 10) > lower / (pos * 10)) {
|
||||
pos *= 10;
|
||||
}
|
||||
int mantissaRight = mantissa % pos;
|
||||
if (mantissaRight >= error / 2) {
|
||||
pos /= 10;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
private static int findUpperDistanceToZero(int mantissa, int error) {
|
||||
int pos = 10;
|
||||
while (pos <= error) {
|
||||
private static int findUpperDistance(int mantissa, int upper) {
|
||||
int pos = 1;
|
||||
while (mantissa / (pos * 10) < upper / (pos * 10)) {
|
||||
pos *= 10;
|
||||
}
|
||||
int mantissaRight = mantissa % pos;
|
||||
if (pos - mantissaRight > error / 2) {
|
||||
pos /= 10;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
static int mulAndShiftRight(int a, int b, int shift) {
|
||||
var result = (a & 0xFFFFFFFFL) * (b & 0xFFFFFFFFL);
|
||||
var nextBit = ((result >> (31 - shift)) & 1) != 0;
|
||||
if (nextBit) {
|
||||
result += 1L << (31 - shift);
|
||||
}
|
||||
return (int) (result >>> (32 - shift));
|
||||
}
|
||||
|
||||
public static class Result {
|
||||
public int mantissa;
|
||||
public int exponent;
|
||||
public boolean sign;
|
||||
}
|
||||
|
||||
// The code below was generated by FloatAnalyzerGenerator
|
||||
|
||||
private static final int[] mantissa10Table = {
|
||||
-18543760,
|
||||
-873828468,
|
||||
-1558056233,
|
||||
-2105438446,
|
||||
-791721136,
|
||||
-1492370368,
|
||||
-2052889754,
|
||||
-707643228,
|
||||
-1425108042,
|
||||
-1999079893,
|
||||
-621547450,
|
||||
-1356231419,
|
||||
-1943978595,
|
||||
-533385374,
|
||||
-1285701758,
|
||||
-1887554866,
|
||||
-443107408,
|
||||
-1213479385,
|
||||
-1829776968,
|
||||
-350662770,
|
||||
-1139523676,
|
||||
-1770612400,
|
||||
-255999462,
|
||||
-1063793029,
|
||||
-1710027882,
|
||||
-159064234,
|
||||
-986244846,
|
||||
-1647989336,
|
||||
-59802560,
|
||||
-906835507,
|
||||
-1584461865,
|
||||
-2126562952,
|
||||
-825520345,
|
||||
-1519409735,
|
||||
-2074521247,
|
||||
-742253618,
|
||||
-1452796353,
|
||||
-2021230542,
|
||||
-656988489,
|
||||
-1384584251,
|
||||
-1966660860,
|
||||
-569676998,
|
||||
-1314735058,
|
||||
-1910781505,
|
||||
-480270031,
|
||||
-1243209484,
|
||||
-1853561046,
|
||||
-388717296,
|
||||
-1169967296,
|
||||
-1794967296,
|
||||
-294967296,
|
||||
-1094967296,
|
||||
-1734967296,
|
||||
-198967296,
|
||||
-1018167296,
|
||||
-1673527296,
|
||||
-100663296,
|
||||
-939524096,
|
||||
-1610612736,
|
||||
-2147483648,
|
||||
-858993460,
|
||||
-1546188227,
|
||||
-2095944041,
|
||||
-776530088,
|
||||
-1480217529,
|
||||
-2043167483,
|
||||
-692087595,
|
||||
-1412663535,
|
||||
-1989124287,
|
||||
-605618482,
|
||||
-1343488245,
|
||||
-1933784055,
|
||||
-517074110,
|
||||
-1272652747,
|
||||
-1877115657,
|
||||
-426404674,
|
||||
-1200117198,
|
||||
-1819087218,
|
||||
-333559171,
|
||||
-1125840796,
|
||||
-1759666096,
|
||||
-238485376,
|
||||
-1049781760,
|
||||
-1698818867,
|
||||
-141129810,
|
||||
-971897307,
|
||||
-1636511305,
|
||||
-41437710,
|
||||
-892143627,
|
||||
-1572708361,
|
||||
-2117160148,
|
||||
-810475859,
|
||||
-1507374147,
|
||||
-2064892777,
|
||||
-726848065,
|
||||
-1440471911,
|
||||
-2011370988,
|
||||
-641213203,
|
||||
-1371964022,
|
||||
-1956564688,
|
||||
};
|
||||
|
||||
private static final int[] exp10Table = {
|
||||
-37,
|
||||
-34,
|
||||
-31,
|
||||
-28,
|
||||
-24,
|
||||
-21,
|
||||
-18,
|
||||
-14,
|
||||
-11,
|
||||
-8,
|
||||
-4,
|
||||
-1,
|
||||
2,
|
||||
6,
|
||||
9,
|
||||
12,
|
||||
16,
|
||||
19,
|
||||
22,
|
||||
26,
|
||||
29,
|
||||
32,
|
||||
36,
|
||||
39,
|
||||
42,
|
||||
46,
|
||||
49,
|
||||
52,
|
||||
56,
|
||||
59,
|
||||
62,
|
||||
65,
|
||||
69,
|
||||
72,
|
||||
75,
|
||||
79,
|
||||
82,
|
||||
85,
|
||||
89,
|
||||
92,
|
||||
95,
|
||||
99,
|
||||
102,
|
||||
105,
|
||||
109,
|
||||
112,
|
||||
115,
|
||||
119,
|
||||
122,
|
||||
125,
|
||||
129,
|
||||
132,
|
||||
135,
|
||||
139,
|
||||
142,
|
||||
145,
|
||||
149,
|
||||
152,
|
||||
155,
|
||||
158,
|
||||
162,
|
||||
165,
|
||||
168,
|
||||
172,
|
||||
175,
|
||||
178,
|
||||
182,
|
||||
185,
|
||||
188,
|
||||
192,
|
||||
195,
|
||||
198,
|
||||
202,
|
||||
205,
|
||||
208,
|
||||
212,
|
||||
215,
|
||||
218,
|
||||
222,
|
||||
225,
|
||||
228,
|
||||
232,
|
||||
235,
|
||||
238,
|
||||
242,
|
||||
245,
|
||||
248,
|
||||
252,
|
||||
255,
|
||||
258,
|
||||
261,
|
||||
265,
|
||||
268,
|
||||
271,
|
||||
275,
|
||||
278,
|
||||
281,
|
||||
285,
|
||||
288,
|
||||
291,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright 2023 konsoletyper.
|
||||
*
|
||||
* 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.math.BigInteger;
|
||||
|
||||
public final class FloatAnalyzerGenerator {
|
||||
private FloatAnalyzerGenerator() {
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
var mantissaList = new int[100];
|
||||
var expList = new int[100];
|
||||
|
||||
var dec = BigInteger.valueOf(1000000000).shiftLeft(128 + 32);
|
||||
for (var i = 0; i < 50; ++i) {
|
||||
var shift = dec.bitLength() - 32;
|
||||
mantissaList[50 + i] = dec.shiftRight(shift).intValue();
|
||||
dec = dec.divide(BigInteger.valueOf(10));
|
||||
var exp = 128 + 32 - shift;
|
||||
expList[50 + i] = 127 + exp;
|
||||
}
|
||||
|
||||
dec = BigInteger.valueOf(1000000000);
|
||||
for (var i = 1; i <= 50; ++i) {
|
||||
dec = dec.multiply(BigInteger.valueOf(10));
|
||||
var shift = dec.bitLength() - 32;
|
||||
mantissaList[50 - i] = dec.shiftRight(shift).intValue();
|
||||
expList[50 - i] = 127 - shift;
|
||||
}
|
||||
|
||||
System.out.println("[mantissa]");
|
||||
for (var value : mantissaList) {
|
||||
System.out.println(value + ",");
|
||||
}
|
||||
System.out.println();
|
||||
|
||||
System.out.println("[exponent]");
|
||||
for (var value : expList) {
|
||||
System.out.println(value + ",");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,261 @@
|
|||
/*
|
||||
* Copyright 2023 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;
|
||||
|
||||
public final class FloatSynthesizer {
|
||||
private FloatSynthesizer() {
|
||||
}
|
||||
|
||||
public static float synthesizeFloat(int mantissa, int exp, boolean negative) {
|
||||
var indexInTable = FloatAnalyzer.MAX_ABS_DEC_EXP - exp;
|
||||
if (mantissa == 0 || indexInTable > mantissa10Table.length || indexInTable < 0) {
|
||||
return Float.intBitsToFloat(negative ? (1 << 31) : 0);
|
||||
}
|
||||
|
||||
var binMantissa = FloatAnalyzer.mulAndShiftRight(mantissa, mantissa10Table[indexInTable], 0);
|
||||
var binExp = exp10Table[indexInTable] - 1;
|
||||
while ((binMantissa & (-1L << 30L)) != 0) {
|
||||
binMantissa >>>= 1;
|
||||
binExp++;
|
||||
}
|
||||
while (binMantissa < (1L << 29)) {
|
||||
binMantissa <<= 1;
|
||||
binExp--;
|
||||
}
|
||||
binExp += 5;
|
||||
if (binExp >= 255) {
|
||||
return negative ? Float.NEGATIVE_INFINITY : Float.POSITIVE_INFINITY;
|
||||
}
|
||||
binMantissa += 1 << 5;
|
||||
if (binExp <= 0) {
|
||||
binMantissa >>= Math.min(-binExp + 1, 32);
|
||||
binExp = 0;
|
||||
}
|
||||
|
||||
binMantissa = (binMantissa >>> 6) & (-1 << 9 >>> 9);
|
||||
var iee754 = binMantissa | (binExp << 23);
|
||||
if (negative) {
|
||||
iee754 ^= 1 << 31;
|
||||
}
|
||||
return Float.intBitsToFloat(iee754);
|
||||
}
|
||||
|
||||
private static final int[] mantissa10Table = {
|
||||
-1213479385,
|
||||
-1829776968,
|
||||
-350662770,
|
||||
-1139523676,
|
||||
-1770612400,
|
||||
-255999462,
|
||||
-1063793029,
|
||||
-1710027882,
|
||||
-159064234,
|
||||
-986244846,
|
||||
-1647989336,
|
||||
-59802560,
|
||||
-906835507,
|
||||
-1584461865,
|
||||
-2126562952,
|
||||
-825520345,
|
||||
-1519409735,
|
||||
-2074521247,
|
||||
-742253618,
|
||||
-1452796353,
|
||||
-2021230542,
|
||||
-656988489,
|
||||
-1384584251,
|
||||
-1966660860,
|
||||
-569676998,
|
||||
-1314735058,
|
||||
-1910781505,
|
||||
-480270031,
|
||||
-1243209484,
|
||||
-1853561046,
|
||||
-388717296,
|
||||
-1169967296,
|
||||
-1794967296,
|
||||
-294967296,
|
||||
-1094967296,
|
||||
-1734967296,
|
||||
-198967296,
|
||||
-1018167296,
|
||||
-1673527296,
|
||||
-100663296,
|
||||
-939524096,
|
||||
-1610612736,
|
||||
-2147483648,
|
||||
-858993460,
|
||||
-1546188227,
|
||||
-2095944041,
|
||||
-776530088,
|
||||
-1480217529,
|
||||
-2043167483,
|
||||
-692087595,
|
||||
-1412663535,
|
||||
-1989124287,
|
||||
-605618482,
|
||||
-1343488245,
|
||||
-1933784055,
|
||||
-517074110,
|
||||
-1272652747,
|
||||
-1877115657,
|
||||
-426404674,
|
||||
-1200117198,
|
||||
-1819087218,
|
||||
-333559171,
|
||||
-1125840796,
|
||||
-1759666096,
|
||||
-238485376,
|
||||
-1049781760,
|
||||
-1698818867,
|
||||
-141129810,
|
||||
-971897307,
|
||||
-1636511305,
|
||||
-41437710,
|
||||
-892143627,
|
||||
-1572708361,
|
||||
-2117160148,
|
||||
-810475859,
|
||||
-1507374147,
|
||||
-2064892777,
|
||||
-726848065,
|
||||
-1440471911,
|
||||
-2011370988,
|
||||
-641213203,
|
||||
-1371964022,
|
||||
-1956564677,
|
||||
-553523105,
|
||||
-1301811943,
|
||||
-1900443014,
|
||||
-463728444,
|
||||
-1229976215,
|
||||
-1842974431,
|
||||
-371778712,
|
||||
-1156416429,
|
||||
-1784126602,
|
||||
-277622186,
|
||||
-1081091208,
|
||||
-1723866426,
|
||||
-181205903,
|
||||
-1003958182,
|
||||
-1662160005,
|
||||
-82475630,
|
||||
-924973963,
|
||||
};
|
||||
|
||||
private static int[] exp10Table = {
|
||||
292,
|
||||
289,
|
||||
285,
|
||||
282,
|
||||
279,
|
||||
275,
|
||||
272,
|
||||
269,
|
||||
265,
|
||||
262,
|
||||
259,
|
||||
255,
|
||||
252,
|
||||
249,
|
||||
246,
|
||||
242,
|
||||
239,
|
||||
236,
|
||||
232,
|
||||
229,
|
||||
226,
|
||||
222,
|
||||
219,
|
||||
216,
|
||||
212,
|
||||
209,
|
||||
206,
|
||||
202,
|
||||
199,
|
||||
196,
|
||||
192,
|
||||
189,
|
||||
186,
|
||||
182,
|
||||
179,
|
||||
176,
|
||||
172,
|
||||
169,
|
||||
166,
|
||||
162,
|
||||
159,
|
||||
156,
|
||||
153,
|
||||
149,
|
||||
146,
|
||||
143,
|
||||
139,
|
||||
136,
|
||||
133,
|
||||
129,
|
||||
126,
|
||||
123,
|
||||
119,
|
||||
116,
|
||||
113,
|
||||
109,
|
||||
106,
|
||||
103,
|
||||
99,
|
||||
96,
|
||||
93,
|
||||
89,
|
||||
86,
|
||||
83,
|
||||
79,
|
||||
76,
|
||||
73,
|
||||
69,
|
||||
66,
|
||||
63,
|
||||
59,
|
||||
56,
|
||||
53,
|
||||
50,
|
||||
46,
|
||||
43,
|
||||
40,
|
||||
36,
|
||||
33,
|
||||
30,
|
||||
26,
|
||||
23,
|
||||
20,
|
||||
16,
|
||||
13,
|
||||
10,
|
||||
6,
|
||||
3,
|
||||
0,
|
||||
-4,
|
||||
-7,
|
||||
-10,
|
||||
-14,
|
||||
-17,
|
||||
-20,
|
||||
-24,
|
||||
-27,
|
||||
-30,
|
||||
-34,
|
||||
-37,
|
||||
};
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright 2023 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.math.BigInteger;
|
||||
|
||||
public final class FloatSynthesizerGenerator {
|
||||
private FloatSynthesizerGenerator() {
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
var mantissaList = new int[100];
|
||||
var expList = new int[100];
|
||||
var shift = 57;
|
||||
|
||||
var exp = 0;
|
||||
var dec = BigInteger.valueOf(100000000);
|
||||
for (var i = 0; i < 50; ++i) {
|
||||
while (BigInteger.ONE.shiftLeft(shift + exp + 1).divide(dec).bitLength() <= 32) {
|
||||
++exp;
|
||||
}
|
||||
mantissaList[50 + i] = BigInteger.ONE.shiftLeft(shift + exp).divide(dec).intValue();
|
||||
dec = dec.multiply(BigInteger.valueOf(10));
|
||||
expList[50 + i] = 127 - exp;
|
||||
}
|
||||
|
||||
exp = 1;
|
||||
dec = BigInteger.valueOf(100000000).multiply(BigInteger.ONE.shiftLeft(128));
|
||||
var q = BigInteger.valueOf(10L);
|
||||
for (var i = 1; i <= 50; ++i) {
|
||||
while (BigInteger.ONE.shiftLeft(shift + 128 - exp).multiply(q).divide(dec).bitLength() > 32) {
|
||||
++exp;
|
||||
}
|
||||
mantissaList[50 - i] = BigInteger.ONE.shiftLeft(shift + 128 - exp).multiply(q).divide(dec).intValue();
|
||||
q = q.multiply(BigInteger.valueOf(10));
|
||||
expList[50 - i] = 127 + exp;
|
||||
}
|
||||
|
||||
System.out.println("[mantissa]");
|
||||
for (var value : mantissaList) {
|
||||
System.out.println(value + ",");
|
||||
}
|
||||
System.out.println();
|
||||
|
||||
System.out.println("[exponent]");
|
||||
for (var value : expList) {
|
||||
System.out.println(value + ",");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -206,13 +206,13 @@ class TAbstractStringBuilder implements TSerializable, TCharSequence {
|
|||
buffer[target++] = '.';
|
||||
buffer[target++] = '0';
|
||||
return this;
|
||||
} else if (TFloat.isNaN(value)) {
|
||||
} else if (Float.isNaN(value)) {
|
||||
insertSpace(target, target + 3);
|
||||
buffer[target++] = 'N';
|
||||
buffer[target++] = 'a';
|
||||
buffer[target++] = 'N';
|
||||
return this;
|
||||
} else if (TFloat.isInfinite(value)) {
|
||||
} else if (Float.isInfinite(value)) {
|
||||
if (value > 0) {
|
||||
insertSpace(target, target + 8);
|
||||
} else {
|
||||
|
@ -230,7 +230,7 @@ class TAbstractStringBuilder implements TSerializable, TCharSequence {
|
|||
return this;
|
||||
}
|
||||
|
||||
FloatAnalyzer.Result number = Constants.floatAnalysisResult;
|
||||
var number = Constants.floatAnalysisResult;
|
||||
FloatAnalyzer.analyze(value, number);
|
||||
int mantissa = number.mantissa;
|
||||
int exp = number.exponent;
|
||||
|
@ -247,6 +247,8 @@ class TAbstractStringBuilder implements TSerializable, TCharSequence {
|
|||
if (zeros > 0) {
|
||||
digits -= zeros;
|
||||
}
|
||||
var leadingZeros = 0;
|
||||
var leadingZero = false;
|
||||
|
||||
// Handle special case of exponent close to 0
|
||||
if (exp < 7 && exp >= -3) {
|
||||
|
@ -255,8 +257,10 @@ class TAbstractStringBuilder implements TSerializable, TCharSequence {
|
|||
digits = Math.max(digits, intPart + 1);
|
||||
exp = 0;
|
||||
} else {
|
||||
mantissa /= Constants.intPowersOfTen[-exp];
|
||||
digits -= exp;
|
||||
intPart = 0;
|
||||
leadingZeros = -exp - 1;
|
||||
leadingZero = true;
|
||||
sz++;
|
||||
exp = 0;
|
||||
}
|
||||
}
|
||||
|
@ -275,7 +279,7 @@ class TAbstractStringBuilder implements TSerializable, TCharSequence {
|
|||
if (exp != 0 && digits == intPart) {
|
||||
digits++;
|
||||
}
|
||||
sz += digits;
|
||||
sz += digits + leadingZeros;
|
||||
|
||||
// Print mantissa
|
||||
insertSpace(target, target + sz);
|
||||
|
@ -283,6 +287,13 @@ class TAbstractStringBuilder implements TSerializable, TCharSequence {
|
|||
buffer[target++] = '-';
|
||||
}
|
||||
int pos = FloatAnalyzer.MAX_POS;
|
||||
if (leadingZero) {
|
||||
buffer[target++] = '0';
|
||||
buffer[target++] = '.';
|
||||
while (leadingZeros-- > 0) {
|
||||
buffer[target++] = '0';
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < digits; ++i) {
|
||||
int intDigit;
|
||||
if (pos > 0) {
|
||||
|
@ -331,13 +342,13 @@ class TAbstractStringBuilder implements TSerializable, TCharSequence {
|
|||
buffer[target++] = '.';
|
||||
buffer[target++] = '0';
|
||||
return this;
|
||||
} else if (TDouble.isNaN(value)) {
|
||||
} else if (Double.isNaN(value)) {
|
||||
insertSpace(target, target + 3);
|
||||
buffer[target++] = 'N';
|
||||
buffer[target++] = 'a';
|
||||
buffer[target++] = 'N';
|
||||
return this;
|
||||
} else if (TDouble.isInfinite(value)) {
|
||||
} else if (Double.isInfinite(value)) {
|
||||
if (value > 0) {
|
||||
insertSpace(target, target + 8);
|
||||
} else {
|
||||
|
@ -355,7 +366,7 @@ class TAbstractStringBuilder implements TSerializable, TCharSequence {
|
|||
return this;
|
||||
}
|
||||
|
||||
DoubleAnalyzer.Result number = Constants.doubleAnalysisResult;
|
||||
var number = Constants.doubleAnalysisResult;
|
||||
DoubleAnalyzer.analyze(value, number);
|
||||
long mantissa = number.mantissa;
|
||||
int exp = number.exponent;
|
||||
|
@ -374,6 +385,8 @@ class TAbstractStringBuilder implements TSerializable, TCharSequence {
|
|||
if (zeros > 0) {
|
||||
digits -= zeros;
|
||||
}
|
||||
var leadingZeros = 0;
|
||||
var leadingZero = false;
|
||||
|
||||
// Handle special case of exponent close to 0
|
||||
if (exp < 7 && exp >= -3) {
|
||||
|
@ -382,8 +395,10 @@ class TAbstractStringBuilder implements TSerializable, TCharSequence {
|
|||
digits = Math.max(digits, intPart + 1);
|
||||
exp = 0;
|
||||
} else {
|
||||
mantissa /= Constants.longPowersOfTen[-exp];
|
||||
digits -= exp;
|
||||
intPart = 0;
|
||||
leadingZeros = -exp - 1;
|
||||
leadingZero = true;
|
||||
sz++;
|
||||
exp = 0;
|
||||
}
|
||||
}
|
||||
|
@ -405,7 +420,7 @@ class TAbstractStringBuilder implements TSerializable, TCharSequence {
|
|||
if (exp != 0 && digits == intPart) {
|
||||
digits++;
|
||||
}
|
||||
sz += digits;
|
||||
sz += digits + leadingZeros;
|
||||
|
||||
// Print mantissa
|
||||
insertSpace(target, target + sz);
|
||||
|
@ -413,6 +428,13 @@ class TAbstractStringBuilder implements TSerializable, TCharSequence {
|
|||
buffer[target++] = '-';
|
||||
}
|
||||
long pos = DoubleAnalyzer.DOUBLE_MAX_POS;
|
||||
if (leadingZero) {
|
||||
buffer[target++] = '0';
|
||||
buffer[target++] = '.';
|
||||
while (leadingZeros-- > 0) {
|
||||
buffer[target++] = '0';
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < digits; ++i) {
|
||||
int intDigit;
|
||||
if (pos > 0) {
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package org.teavm.classlib.java.lang;
|
||||
|
||||
import org.teavm.backend.javascript.spi.InjectedBy;
|
||||
import org.teavm.classlib.impl.text.DoubleSynthesizer;
|
||||
import org.teavm.interop.Import;
|
||||
import org.teavm.interop.NoSideEffects;
|
||||
import org.teavm.interop.Unmanaged;
|
||||
|
@ -25,7 +26,7 @@ import org.teavm.jso.JSBody;
|
|||
public class TDouble extends TNumber implements TComparable<TDouble> {
|
||||
public static final double POSITIVE_INFINITY = 1 / 0.0;
|
||||
public static final double NEGATIVE_INFINITY = -POSITIVE_INFINITY;
|
||||
public static final double NaN = getNaN();
|
||||
public static final double NaN = 0 / 0.0;
|
||||
public static final double MAX_VALUE = 0x1.FFFFFFFFFFFFFP+1023;
|
||||
public static final double MIN_NORMAL = -0x1.0P+1022;
|
||||
public static final double MIN_VALUE = 0x0.0000000000001P-1022;
|
||||
|
@ -39,7 +40,7 @@ public class TDouble extends TNumber implements TComparable<TDouble> {
|
|||
this.value = value;
|
||||
}
|
||||
|
||||
public TDouble(TString value) throws TNumberFormatException {
|
||||
public TDouble(String value) throws TNumberFormatException {
|
||||
this.value = parseDouble(value);
|
||||
}
|
||||
|
||||
|
@ -71,21 +72,21 @@ public class TDouble extends TNumber implements TComparable<TDouble> {
|
|||
return new TStringBuilder().append(d).toString();
|
||||
}
|
||||
|
||||
public static TDouble valueOf(TString string) {
|
||||
public static TDouble valueOf(String string) {
|
||||
return valueOf(parseDouble(string));
|
||||
}
|
||||
|
||||
public static double parseDouble(TString string) throws TNumberFormatException {
|
||||
public static double parseDouble(String string) throws NumberFormatException {
|
||||
// TODO: parse infinite and different radix
|
||||
|
||||
if (string.isEmpty()) {
|
||||
throw new TNumberFormatException();
|
||||
throw new NumberFormatException();
|
||||
}
|
||||
int start = 0;
|
||||
int end = string.length();
|
||||
while (string.charAt(start) <= ' ') {
|
||||
if (++start == end) {
|
||||
throw new TNumberFormatException();
|
||||
throw new NumberFormatException();
|
||||
}
|
||||
}
|
||||
while (string.charAt(end - 1) <= ' ') {
|
||||
|
@ -101,17 +102,18 @@ public class TDouble extends TNumber implements TComparable<TDouble> {
|
|||
++index;
|
||||
}
|
||||
if (index == end) {
|
||||
throw new TNumberFormatException();
|
||||
throw new NumberFormatException();
|
||||
}
|
||||
char c = string.charAt(index);
|
||||
|
||||
long mantissa = 0;
|
||||
int exp = 0;
|
||||
int exp = -1;
|
||||
boolean hasOneDigit = false;
|
||||
long mantissaPos = 1000000000000000000L;
|
||||
if (c != '.') {
|
||||
hasOneDigit = true;
|
||||
if (c < '0' || c > '9') {
|
||||
throw new TNumberFormatException();
|
||||
throw new NumberFormatException();
|
||||
}
|
||||
while (index < end && string.charAt(index) == '0') {
|
||||
++index;
|
||||
|
@ -121,11 +123,11 @@ public class TDouble extends TNumber implements TComparable<TDouble> {
|
|||
if (c < '0' || c > '9') {
|
||||
break;
|
||||
}
|
||||
if (mantissa < TLong.MAX_VALUE / 10 - 9) {
|
||||
mantissa = mantissa * 10 + (c - '0');
|
||||
} else {
|
||||
++exp;
|
||||
if (mantissaPos > 0) {
|
||||
mantissa = mantissa + (mantissaPos * (c - '0'));
|
||||
mantissaPos = Long.divideUnsigned(mantissaPos, 10);
|
||||
}
|
||||
++exp;
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
@ -136,26 +138,28 @@ public class TDouble extends TNumber implements TComparable<TDouble> {
|
|||
if (c < '0' || c > '9') {
|
||||
break;
|
||||
}
|
||||
if (mantissa < TLong.MAX_VALUE / 10 - 9) {
|
||||
mantissa = mantissa * 10 + (c - '0');
|
||||
--exp;
|
||||
if (mantissa == 0 && c == '0') {
|
||||
exp--;
|
||||
} else if (mantissaPos > 0) {
|
||||
mantissa = mantissa + (mantissaPos * (c - '0'));
|
||||
mantissaPos = Long.divideUnsigned(mantissaPos, 10);
|
||||
}
|
||||
++index;
|
||||
hasOneDigit = true;
|
||||
}
|
||||
if (!hasOneDigit) {
|
||||
throw new TNumberFormatException();
|
||||
throw new NumberFormatException();
|
||||
}
|
||||
}
|
||||
if (index < end) {
|
||||
c = string.charAt(index);
|
||||
if (c != 'e' && c != 'E') {
|
||||
throw new TNumberFormatException();
|
||||
throw new NumberFormatException();
|
||||
}
|
||||
++index;
|
||||
boolean negativeExp = false;
|
||||
if (index == end) {
|
||||
throw new TNumberFormatException();
|
||||
throw new NumberFormatException();
|
||||
}
|
||||
if (string.charAt(index) == '-') {
|
||||
++index;
|
||||
|
@ -175,39 +179,15 @@ public class TDouble extends TNumber implements TComparable<TDouble> {
|
|||
++index;
|
||||
}
|
||||
if (!hasOneDigit) {
|
||||
throw new TNumberFormatException();
|
||||
throw new NumberFormatException();
|
||||
}
|
||||
if (negativeExp) {
|
||||
numExp = -numExp;
|
||||
}
|
||||
exp += numExp;
|
||||
}
|
||||
if (exp > 308 || exp == 308 && mantissa > 17976931348623157L) {
|
||||
return !negative ? POSITIVE_INFINITY : NEGATIVE_INFINITY;
|
||||
}
|
||||
if (negative) {
|
||||
mantissa = -mantissa;
|
||||
}
|
||||
return mantissa * decimalExponent(exp);
|
||||
}
|
||||
|
||||
public static double decimalExponent(int n) {
|
||||
double d;
|
||||
if (n < 0) {
|
||||
d = 0.1;
|
||||
n = -n;
|
||||
} else {
|
||||
d = 10;
|
||||
}
|
||||
double result = 1;
|
||||
while (n != 0) {
|
||||
if (n % 2 != 0) {
|
||||
result *= d;
|
||||
}
|
||||
d *= d;
|
||||
n /= 2;
|
||||
}
|
||||
return result;
|
||||
return DoubleSynthesizer.synthesizeDouble(mantissa, exp, negative);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -255,12 +235,6 @@ public class TDouble extends TNumber implements TComparable<TDouble> {
|
|||
@Unmanaged
|
||||
public static native boolean isNaN(double v);
|
||||
|
||||
@JSBody(script = "return NaN;")
|
||||
@Import(module = "teavm", name = "teavm_getNaN")
|
||||
@NoSideEffects
|
||||
@Unmanaged
|
||||
private static native double getNaN();
|
||||
|
||||
@JSBody(params = "v", script = "return !isFinite(v);")
|
||||
@Import(module = "teavm", name = "isinf")
|
||||
@NoSideEffects
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package org.teavm.classlib.java.lang;
|
||||
|
||||
import org.teavm.classlib.impl.text.FloatSynthesizer;
|
||||
import org.teavm.interop.Import;
|
||||
import org.teavm.interop.NoSideEffects;
|
||||
import org.teavm.interop.Unmanaged;
|
||||
|
@ -23,7 +24,7 @@ import org.teavm.jso.JSBody;
|
|||
public class TFloat extends TNumber implements TComparable<TFloat> {
|
||||
public static final float POSITIVE_INFINITY = 1 / 0.0f;
|
||||
public static final float NEGATIVE_INFINITY = -POSITIVE_INFINITY;
|
||||
public static final float NaN = getNaN();
|
||||
public static final float NaN = 0f / 0f;
|
||||
public static final float MAX_VALUE = 0x1.fffffeP+127f;
|
||||
public static final float MIN_VALUE = 0x1.0p-126f;
|
||||
public static final float MIN_NORMAL = 0x0.000002P-126f;
|
||||
|
@ -41,7 +42,7 @@ public class TFloat extends TNumber implements TComparable<TFloat> {
|
|||
this((float) value);
|
||||
}
|
||||
|
||||
public TFloat(TString value) throws TNumberFormatException {
|
||||
public TFloat(String value) throws TNumberFormatException {
|
||||
this(parseFloat(value));
|
||||
}
|
||||
|
||||
|
@ -113,13 +114,7 @@ public class TFloat extends TNumber implements TComparable<TFloat> {
|
|||
@Unmanaged
|
||||
public static native boolean isFinite(float v);
|
||||
|
||||
@JSBody(script = "return NaN;")
|
||||
@Import(module = "teavm", name = "teavm_getNaN")
|
||||
@NoSideEffects
|
||||
@Unmanaged
|
||||
private static native float getNaN();
|
||||
|
||||
public static float parseFloat(TString string) throws TNumberFormatException {
|
||||
public static float parseFloat(String string) throws TNumberFormatException {
|
||||
// TODO: parse infinite and different radix
|
||||
|
||||
if (string.isEmpty()) {
|
||||
|
@ -150,7 +145,8 @@ public class TFloat extends TNumber implements TComparable<TFloat> {
|
|||
char c = string.charAt(index);
|
||||
|
||||
int mantissa = 0;
|
||||
int exp = 0;
|
||||
int exp = -1;
|
||||
int mantissaPos = 100000000;
|
||||
|
||||
boolean hasOneDigit = false;
|
||||
if (c != '.') {
|
||||
|
@ -167,11 +163,11 @@ public class TFloat extends TNumber implements TComparable<TFloat> {
|
|||
if (c < '0' || c > '9') {
|
||||
break;
|
||||
}
|
||||
if (mantissa < (TInteger.MAX_VALUE / 10) - 9) {
|
||||
mantissa = mantissa * 10 + (c - '0');
|
||||
} else {
|
||||
++exp;
|
||||
if (mantissaPos > 0) {
|
||||
mantissa = mantissa + (mantissaPos * (c - '0'));
|
||||
mantissaPos = Integer.divideUnsigned(mantissaPos, 10);
|
||||
}
|
||||
++exp;
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
@ -183,9 +179,11 @@ public class TFloat extends TNumber implements TComparable<TFloat> {
|
|||
if (c < '0' || c > '9') {
|
||||
break;
|
||||
}
|
||||
if (mantissa < (TInteger.MAX_VALUE / 10) - 9) {
|
||||
mantissa = mantissa * 10 + (c - '0');
|
||||
--exp;
|
||||
if (mantissa == 0 && c == '0') {
|
||||
exp--;
|
||||
} else if (mantissaPos > 0) {
|
||||
mantissa = mantissa + (mantissaPos * (c - '0'));
|
||||
mantissaPos = Integer.divideUnsigned(mantissaPos, 10);
|
||||
}
|
||||
++index;
|
||||
hasOneDigit = true;
|
||||
|
@ -229,35 +227,11 @@ public class TFloat extends TNumber implements TComparable<TFloat> {
|
|||
}
|
||||
exp += numExp;
|
||||
}
|
||||
if (exp > 38 || exp == 38 && mantissa > 34028234) {
|
||||
return !negative ? POSITIVE_INFINITY : NEGATIVE_INFINITY;
|
||||
}
|
||||
if (negative) {
|
||||
mantissa = -mantissa;
|
||||
}
|
||||
return mantissa * decimalExponent(exp);
|
||||
|
||||
return FloatSynthesizer.synthesizeFloat(mantissa, exp, negative);
|
||||
}
|
||||
|
||||
private static float decimalExponent(int n) {
|
||||
double d;
|
||||
if (n < 0) {
|
||||
d = 0.1;
|
||||
n = -n;
|
||||
} else {
|
||||
d = 10;
|
||||
}
|
||||
double result = 1;
|
||||
while (n != 0) {
|
||||
if (n % 2 != 0) {
|
||||
result *= d;
|
||||
}
|
||||
d *= d;
|
||||
n /= 2;
|
||||
}
|
||||
return (float) result;
|
||||
}
|
||||
|
||||
public static TFloat valueOf(TString s) throws TNumberFormatException {
|
||||
public static TFloat valueOf(String s) throws TNumberFormatException {
|
||||
return valueOf(parseFloat(s));
|
||||
}
|
||||
|
||||
|
|
|
@ -20,10 +20,10 @@ import java.math.BigInteger;
|
|||
import java.text.DecimalFormatSymbols;
|
||||
import java.util.Arrays;
|
||||
import org.teavm.classlib.impl.text.DoubleAnalyzer;
|
||||
import org.teavm.classlib.impl.text.DoubleSynthesizer;
|
||||
import org.teavm.classlib.impl.text.FloatAnalyzer;
|
||||
import org.teavm.classlib.impl.unicode.CLDRHelper;
|
||||
import org.teavm.classlib.java.lang.TArithmeticException;
|
||||
import org.teavm.classlib.java.lang.TDouble;
|
||||
import org.teavm.classlib.java.util.TLocale;
|
||||
|
||||
public class TDecimalFormat extends TNumberFormat {
|
||||
|
@ -494,15 +494,22 @@ public class TDecimalFormat extends TNumberFormat {
|
|||
}
|
||||
}
|
||||
|
||||
// Expose result
|
||||
if (mantissa == 0 && !positive) {
|
||||
return -0.0;
|
||||
}
|
||||
if (exponent == 0) {
|
||||
return positive ? mantissa : -mantissa;
|
||||
}
|
||||
double result = TDouble.decimalExponent(exponent) * mantissa;
|
||||
return positive ? result : -result;
|
||||
|
||||
exponent += 18;
|
||||
if (mantissa != 0) {
|
||||
while (mantissa / 1000000000000000000L == 0) {
|
||||
mantissa *= 10;
|
||||
exponent--;
|
||||
}
|
||||
}
|
||||
|
||||
return DoubleSynthesizer.synthesizeDouble(mantissa, exponent, !positive);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright 2023 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;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import org.junit.Test;
|
||||
import org.teavm.classlib.java.lang.TDouble;
|
||||
|
||||
public class DoubleParseTest {
|
||||
@Test
|
||||
public void parse() {
|
||||
assertEquals(1, TDouble.parseDouble("1"), 1E-12);
|
||||
assertEquals(23, TDouble.parseDouble("23"), 1E-12);
|
||||
assertEquals(23, TDouble.parseDouble("23.0"), 1E-12);
|
||||
assertEquals(23, TDouble.parseDouble("23E0"), 1E-12);
|
||||
assertEquals(23, TDouble.parseDouble("2.30000E1"), 1E-12);
|
||||
assertEquals(23, TDouble.parseDouble("0.23E2"), 1E-12);
|
||||
assertEquals(23, TDouble.parseDouble("0.000023E6"), 1E-12);
|
||||
assertEquals(23, TDouble.parseDouble("00230000e-4"), 1E-12);
|
||||
assertEquals(23, TDouble.parseDouble("2300000000000000000000e-20"), 1E-12);
|
||||
assertEquals(23, TDouble.parseDouble("2300000000000000000000e-20"), 1E-12);
|
||||
assertEquals(23, TDouble.parseDouble("23."), 1E-12);
|
||||
assertEquals(0.1, TDouble.parseDouble("0.1"), 0.001);
|
||||
assertEquals(0.1, TDouble.parseDouble(".1"), 0.001);
|
||||
assertEquals(0.1, TDouble.parseDouble(" .1"), 0.001);
|
||||
assertEquals(0.1, TDouble.parseDouble(".1 "), 0.001);
|
||||
assertEquals(-23, TDouble.parseDouble("-23"), 1E-12);
|
||||
assertEquals(0, TDouble.parseDouble("0.0"), 1E-12);
|
||||
assertEquals(0, TDouble.parseDouble("0"), 1E-12);
|
||||
assertEquals(0, TDouble.parseDouble("00"), 1E-12);
|
||||
assertEquals(0, TDouble.parseDouble("0."), 1E-12);
|
||||
assertEquals(0, TDouble.parseDouble(".0"), 1E-12);
|
||||
assertEquals(0, TDouble.parseDouble("23E-8000"), 1E-12);
|
||||
assertEquals(0, TDouble.parseDouble("00000"), 1E-12);
|
||||
assertEquals(0, TDouble.parseDouble("00000.0000"), 1E-12);
|
||||
|
||||
assertEquals(9218868437227405312L, Double.doubleToLongBits(TDouble.parseDouble("7.17917199420887E309")));
|
||||
assertEquals(3823970227817620L, Double.doubleToLongBits(TDouble.parseDouble("1.889292320284411E-308")));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright 2023 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;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import org.junit.Test;
|
||||
import org.teavm.classlib.java.lang.TStringBuilder;
|
||||
|
||||
public class DoubleWriteTest {
|
||||
@Test
|
||||
public void lowerUpperEquidistant() {
|
||||
var d = Double.longBitsToDouble(1551003667821398223L);
|
||||
assertEquals("5.5497208273234845E-205", toString(d));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void absLowNegativeExponent() {
|
||||
var d = Double.longBitsToDouble(-4656062020850909140L);
|
||||
assertEquals("-0.0022393517064785064", toString(d));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void values() {
|
||||
assertEquals("1.23456789E150", toString(1.23456789E150));
|
||||
assertEquals("10.0", toString(10.0));
|
||||
assertEquals("20.0", toString(20.0));
|
||||
assertEquals("100.0", toString(100.0));
|
||||
assertEquals("1000.0", toString(1000.0));
|
||||
assertEquals("0.1", toString(0.1));
|
||||
assertEquals("0.01", toString(0.01));
|
||||
assertEquals("0.001", toString(0.001));
|
||||
assertEquals("1.0E20", toString(1e20));
|
||||
assertEquals("2.0E20", toString(2e20));
|
||||
assertEquals("1.0E-12", toString(1e-12));
|
||||
assertEquals("-1.23456789E150", toString(-1.23456789E150));
|
||||
assertEquals("1.23456789E-150", toString(1.23456789E-150));
|
||||
assertEquals("1.79769313486231E308", toString(1.79769313486231E308));
|
||||
assertEquals("3.0E-308", toString(3E-308));
|
||||
assertEquals("1200.0", toString(1200.0));
|
||||
assertEquals("0.023", toString(0.023));
|
||||
assertEquals("0.0", toString(0.0));
|
||||
assertEquals("1.0", toString(1.0));
|
||||
}
|
||||
|
||||
private String toString(double d) {
|
||||
return new TStringBuilder().append(d).toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright 2023 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;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import org.junit.Test;
|
||||
import org.teavm.classlib.java.lang.TFloat;
|
||||
|
||||
public class FloatParseTest {
|
||||
@Test
|
||||
public void parse() {
|
||||
assertEquals(1, TFloat.parseFloat("1"), 1E-12f);
|
||||
assertEquals(23, TFloat.parseFloat("23"), 1E-12F);
|
||||
assertEquals(23, TFloat.parseFloat("23.0"), 1E-12F);
|
||||
assertEquals(23, TFloat.parseFloat("23E0"), 1E-12F);
|
||||
assertEquals(23, TFloat.parseFloat("2.30000E1"), 1E-12F);
|
||||
assertEquals(23, TFloat.parseFloat("0.23E2"), 1E-12F);
|
||||
assertEquals(23, TFloat.parseFloat("0.000023E6"), 1E-12F);
|
||||
assertEquals(23, TFloat.parseFloat("00230000e-4"), 1E-12F);
|
||||
assertEquals(23, TFloat.parseFloat("2300000000000000000000e-20"), 1E-12F);
|
||||
assertEquals(23, TFloat.parseFloat("2300000000000000000000e-20"), 1E-12F);
|
||||
assertEquals(23, TFloat.parseFloat("2300000000000000000000e-20"), 1E-12F);
|
||||
assertEquals(23, TFloat.parseFloat("23."), 1E-12F);
|
||||
assertEquals(0.1F, TFloat.parseFloat("0.1"), 0.001F);
|
||||
assertEquals(0.1F, TFloat.parseFloat(".1"), 0.001F);
|
||||
assertEquals(0.1F, TFloat.parseFloat(" .1"), 0.001F);
|
||||
assertEquals(0.1F, TFloat.parseFloat(".1 "), 0.001F);
|
||||
assertEquals(-23, TFloat.parseFloat("-23"), 1E-12F);
|
||||
assertEquals(0, TFloat.parseFloat("0.0"), 1E-12F);
|
||||
assertEquals(0, TFloat.parseFloat("0"), 1E-12F);
|
||||
assertEquals(0, TFloat.parseFloat("00"), 1E-12F);
|
||||
assertEquals(0, TFloat.parseFloat(".0"), 1E-12F);
|
||||
assertEquals(0, TFloat.parseFloat("0."), 1E-12F);
|
||||
assertEquals(0, TFloat.parseFloat("23E-8000"), 1E-12F);
|
||||
assertEquals(0, TFloat.parseFloat("00000"), 1E-12F);
|
||||
assertEquals(0, TFloat.parseFloat("00000.0000"), 1E-12F);
|
||||
assertEquals(4499999285F, TFloat.parseFloat("4499999285"), 100F);
|
||||
assertEquals(0.4499999285F, TFloat.parseFloat("0.4499999285"), 1E-9F);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright 2023 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;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import org.junit.Test;
|
||||
import org.teavm.classlib.java.lang.TStringBuilder;
|
||||
|
||||
public class FloatWriteTest {
|
||||
@Test
|
||||
public void values() {
|
||||
assertEquals("1.234E25", toString(1.234E25F));
|
||||
assertEquals("9.8765E30", toString(9.8765E30F));
|
||||
assertEquals("-1.234E25", toString(-1.234E25F));
|
||||
assertEquals("-9.8765E30", toString(-9.8765E30F));
|
||||
assertEquals("3.402823E38", toString(3.402823E38f));
|
||||
assertEquals("9.8764E-30", toString(9.8764E-30F));
|
||||
assertEquals("-1.234E-25", toString(-1.234E-25F));
|
||||
assertEquals("-9.8764E-30", toString(-9.8764E-30F));
|
||||
assertEquals("1.17549E-38", toString(1.17549E-38f));
|
||||
assertEquals("1200.0", toString(1200f));
|
||||
assertEquals("0.023", toString(0.023f));
|
||||
assertEquals("0.0", toString(0f));
|
||||
assertEquals("1.0", toString(1f));
|
||||
}
|
||||
|
||||
private String toString(float f) {
|
||||
return new TStringBuilder().append(f).toString();
|
||||
}
|
||||
}
|
|
@ -151,10 +151,6 @@ static inline void* teavm_checkcast(void* obj, int32_t (*cls)(TeaVM_Class*)) {
|
|||
|
||||
extern double teavm_rand();
|
||||
|
||||
static inline float teavm_getNaN() {
|
||||
return NAN;
|
||||
}
|
||||
|
||||
extern void teavm_beforeInit();
|
||||
extern void teavm_afterInitClasses();
|
||||
|
||||
|
|
|
@ -15,10 +15,6 @@ static int32_t wasm_heap_size;
|
|||
static int wasm_args;
|
||||
static char** wasm_argv;
|
||||
|
||||
float teavm_teavm_getNaN() {
|
||||
return NAN;
|
||||
}
|
||||
|
||||
#define teavmMath_sin sin
|
||||
#define teavmMath_cos cos
|
||||
#define teavmMath_sqrt sqrt
|
||||
|
|
|
@ -68,6 +68,11 @@ gradle.allprojects {
|
|||
tasks.withType<Javadoc>().configureEach {
|
||||
options.encoding = "UTF-8"
|
||||
}
|
||||
tasks.withType<JavaExec>().configureEach {
|
||||
if (name.endsWith("main()")) {
|
||||
notCompatibleWithConfigurationCache("JavaExec created by IntelliJ")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gradle.afterProject {
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.teavm.classlib.java.lang;
|
|||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import java.util.Random;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.teavm.junit.TeaVMTestRunner;
|
||||
|
@ -49,11 +50,28 @@ public class DoubleTest {
|
|||
assertEquals(0, Double.parseDouble("23E-8000"), 1E-12);
|
||||
assertEquals(0, Double.parseDouble("00000"), 1E-12);
|
||||
assertEquals(0, Double.parseDouble("00000.0000"), 1E-12);
|
||||
assertEquals("74.92507492507494", Double.toString(Double.parseDouble("74.92507492507494")));
|
||||
|
||||
assertEquals(4499999999999888888888888.0, Double.parseDouble("4499999999999888888888888"), 1E9);
|
||||
assertEquals(0.4499999999999888888888888, Double.parseDouble("0.4499999999999888888888888"), 1E-15);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void randomDoubles() {
|
||||
var random = new Random();
|
||||
for (var i = 0; i < 10000; ++i) {
|
||||
var n = random.nextLong();
|
||||
var d = Double.longBitsToDouble(n);
|
||||
if (Double.isNaN(d) || Double.isInfinite(d)) {
|
||||
continue;
|
||||
}
|
||||
var actual = Double.parseDouble(Double.toString(d));
|
||||
if (n != Double.doubleToLongBits(actual)) {
|
||||
System.out.println(d + ", " + n + ", " + Double.doubleToLongBits(actual));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parsedWithError() {
|
||||
checkIllegalFormat("");
|
||||
|
|
|
@ -30,9 +30,11 @@ import org.junit.runner.RunWith;
|
|||
import org.teavm.junit.TeaVMProperties;
|
||||
import org.teavm.junit.TeaVMProperty;
|
||||
import org.teavm.junit.TeaVMTestRunner;
|
||||
import org.teavm.junit.WholeClassCompilation;
|
||||
|
||||
@RunWith(TeaVMTestRunner.class)
|
||||
@TeaVMProperties(@TeaVMProperty(key = "java.util.Locale.available", value = "en, en_US, en_GB, ru, ru_RU"))
|
||||
@WholeClassCompilation
|
||||
public class DecimalFormatTest {
|
||||
private static DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.ENGLISH);
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user