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 {
|
teavmPublish {
|
||||||
artifactId = "teavm-classlib"
|
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 final class FloatAnalyzer {
|
||||||
public static final int PRECISION = 9;
|
public static final int PRECISION = 9;
|
||||||
public static final int MAX_POS = 100000000;
|
public static final int MAX_POS = 100000000;
|
||||||
|
static final int MAX_ABS_DEC_EXP = 50;
|
||||||
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];
|
|
||||||
|
|
||||||
private FloatAnalyzer() {
|
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) {
|
public static void analyze(float d, Result result) {
|
||||||
int bits = Float.floatToIntBits(d);
|
int bits = Float.floatToIntBits(d);
|
||||||
result.sign = (bits & (1 << 31)) != 0;
|
result.sign = (bits & (1 << 31)) != 0;
|
||||||
|
@ -83,13 +36,11 @@ public final class FloatAnalyzer {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int errorShift = 0;
|
|
||||||
if (exponent == 0) {
|
if (exponent == 0) {
|
||||||
mantissa <<= 1;
|
mantissa <<= 1;
|
||||||
while ((mantissa & (1L << 23)) == 0) {
|
while ((mantissa & (1L << 23)) == 0) {
|
||||||
mantissa <<= 1;
|
mantissa <<= 1;
|
||||||
exponent--;
|
exponent--;
|
||||||
++errorShift;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
mantissa |= 1 << 23;
|
mantissa |= 1 << 23;
|
||||||
|
@ -97,37 +48,36 @@ public final class FloatAnalyzer {
|
||||||
|
|
||||||
int decExponent = Arrays.binarySearch(exp10Table, exponent);
|
int decExponent = Arrays.binarySearch(exp10Table, exponent);
|
||||||
if (decExponent < 0) {
|
if (decExponent < 0) {
|
||||||
decExponent = -decExponent - 2;
|
decExponent = -decExponent;
|
||||||
}
|
}
|
||||||
int binExponentCorrection = exponent - exp10Table[decExponent];
|
int binExponentCorrection = exponent - exp10Table[decExponent + 1];
|
||||||
int mantissaShift = 9 + binExponentCorrection;
|
int mantissaShift = 9 + binExponentCorrection;
|
||||||
|
|
||||||
int decMantissa = (int) (((long) mantissa * mantissa10Table[decExponent]) >>> (32 - mantissaShift));
|
int decMantissa = mulAndShiftRight(mantissa, mantissa10Table[decExponent + 1], mantissaShift);
|
||||||
if (decMantissa >= 1000000000) {
|
if (decMantissa >= 1000000000) {
|
||||||
++decExponent;
|
++decExponent;
|
||||||
binExponentCorrection = exponent - exp10Table[decExponent];
|
binExponentCorrection = exponent - exp10Table[decExponent + 1];
|
||||||
mantissaShift = 9 + binExponentCorrection;
|
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;
|
var lowerPos = findLowerDistance(decMantissa, decMantissaLow);
|
||||||
int error = errorShift >= 0
|
var upperPos = findUpperDistance(decMantissa, decMantissaHi);
|
||||||
? 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);
|
|
||||||
if (lowerPos > upperPos) {
|
if (lowerPos > upperPos) {
|
||||||
decMantissa = (decMantissa / lowerPos) * lowerPos;
|
decMantissa = (decMantissa / lowerPos) * lowerPos;
|
||||||
} else if (lowerPos < upperPos) {
|
} else if (lowerPos < upperPos) {
|
||||||
decMantissa = (decMantissa / upperPos) * upperPos + upperPos;
|
decMantissa = (decMantissa / upperPos) * upperPos + upperPos;
|
||||||
} else {
|
} else {
|
||||||
decMantissa = ((decMantissa + upperPos / 2) / upperPos) * upperPos;
|
decMantissa = ((decMantissa + (upperPos / 2)) / upperPos) * upperPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (decMantissa >= 1000000000) {
|
if (decMantissa >= 1000000000) {
|
||||||
|
@ -142,33 +92,242 @@ public final class FloatAnalyzer {
|
||||||
result.exponent = decExponent - MAX_ABS_DEC_EXP;
|
result.exponent = decExponent - MAX_ABS_DEC_EXP;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int findLowerDistanceToZero(int mantissa, int error) {
|
private static int findLowerDistance(int mantissa, int lower) {
|
||||||
int pos = 10;
|
int pos = 1;
|
||||||
while (pos <= error) {
|
while (mantissa / (pos * 10) > lower / (pos * 10)) {
|
||||||
pos *= 10;
|
pos *= 10;
|
||||||
}
|
}
|
||||||
int mantissaRight = mantissa % pos;
|
|
||||||
if (mantissaRight >= error / 2) {
|
|
||||||
pos /= 10;
|
|
||||||
}
|
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int findUpperDistanceToZero(int mantissa, int error) {
|
private static int findUpperDistance(int mantissa, int upper) {
|
||||||
int pos = 10;
|
int pos = 1;
|
||||||
while (pos <= error) {
|
while (mantissa / (pos * 10) < upper / (pos * 10)) {
|
||||||
pos *= 10;
|
pos *= 10;
|
||||||
}
|
}
|
||||||
int mantissaRight = mantissa % pos;
|
|
||||||
if (pos - mantissaRight > error / 2) {
|
|
||||||
pos /= 10;
|
|
||||||
}
|
|
||||||
return pos;
|
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 static class Result {
|
||||||
public int mantissa;
|
public int mantissa;
|
||||||
public int exponent;
|
public int exponent;
|
||||||
public boolean sign;
|
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++] = '.';
|
||||||
buffer[target++] = '0';
|
buffer[target++] = '0';
|
||||||
return this;
|
return this;
|
||||||
} else if (TFloat.isNaN(value)) {
|
} else if (Float.isNaN(value)) {
|
||||||
insertSpace(target, target + 3);
|
insertSpace(target, target + 3);
|
||||||
buffer[target++] = 'N';
|
buffer[target++] = 'N';
|
||||||
buffer[target++] = 'a';
|
buffer[target++] = 'a';
|
||||||
buffer[target++] = 'N';
|
buffer[target++] = 'N';
|
||||||
return this;
|
return this;
|
||||||
} else if (TFloat.isInfinite(value)) {
|
} else if (Float.isInfinite(value)) {
|
||||||
if (value > 0) {
|
if (value > 0) {
|
||||||
insertSpace(target, target + 8);
|
insertSpace(target, target + 8);
|
||||||
} else {
|
} else {
|
||||||
|
@ -230,7 +230,7 @@ class TAbstractStringBuilder implements TSerializable, TCharSequence {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
FloatAnalyzer.Result number = Constants.floatAnalysisResult;
|
var number = Constants.floatAnalysisResult;
|
||||||
FloatAnalyzer.analyze(value, number);
|
FloatAnalyzer.analyze(value, number);
|
||||||
int mantissa = number.mantissa;
|
int mantissa = number.mantissa;
|
||||||
int exp = number.exponent;
|
int exp = number.exponent;
|
||||||
|
@ -247,6 +247,8 @@ class TAbstractStringBuilder implements TSerializable, TCharSequence {
|
||||||
if (zeros > 0) {
|
if (zeros > 0) {
|
||||||
digits -= zeros;
|
digits -= zeros;
|
||||||
}
|
}
|
||||||
|
var leadingZeros = 0;
|
||||||
|
var leadingZero = false;
|
||||||
|
|
||||||
// Handle special case of exponent close to 0
|
// Handle special case of exponent close to 0
|
||||||
if (exp < 7 && exp >= -3) {
|
if (exp < 7 && exp >= -3) {
|
||||||
|
@ -255,8 +257,10 @@ class TAbstractStringBuilder implements TSerializable, TCharSequence {
|
||||||
digits = Math.max(digits, intPart + 1);
|
digits = Math.max(digits, intPart + 1);
|
||||||
exp = 0;
|
exp = 0;
|
||||||
} else {
|
} else {
|
||||||
mantissa /= Constants.intPowersOfTen[-exp];
|
intPart = 0;
|
||||||
digits -= exp;
|
leadingZeros = -exp - 1;
|
||||||
|
leadingZero = true;
|
||||||
|
sz++;
|
||||||
exp = 0;
|
exp = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -275,7 +279,7 @@ class TAbstractStringBuilder implements TSerializable, TCharSequence {
|
||||||
if (exp != 0 && digits == intPart) {
|
if (exp != 0 && digits == intPart) {
|
||||||
digits++;
|
digits++;
|
||||||
}
|
}
|
||||||
sz += digits;
|
sz += digits + leadingZeros;
|
||||||
|
|
||||||
// Print mantissa
|
// Print mantissa
|
||||||
insertSpace(target, target + sz);
|
insertSpace(target, target + sz);
|
||||||
|
@ -283,6 +287,13 @@ class TAbstractStringBuilder implements TSerializable, TCharSequence {
|
||||||
buffer[target++] = '-';
|
buffer[target++] = '-';
|
||||||
}
|
}
|
||||||
int pos = FloatAnalyzer.MAX_POS;
|
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) {
|
for (int i = 0; i < digits; ++i) {
|
||||||
int intDigit;
|
int intDigit;
|
||||||
if (pos > 0) {
|
if (pos > 0) {
|
||||||
|
@ -331,13 +342,13 @@ class TAbstractStringBuilder implements TSerializable, TCharSequence {
|
||||||
buffer[target++] = '.';
|
buffer[target++] = '.';
|
||||||
buffer[target++] = '0';
|
buffer[target++] = '0';
|
||||||
return this;
|
return this;
|
||||||
} else if (TDouble.isNaN(value)) {
|
} else if (Double.isNaN(value)) {
|
||||||
insertSpace(target, target + 3);
|
insertSpace(target, target + 3);
|
||||||
buffer[target++] = 'N';
|
buffer[target++] = 'N';
|
||||||
buffer[target++] = 'a';
|
buffer[target++] = 'a';
|
||||||
buffer[target++] = 'N';
|
buffer[target++] = 'N';
|
||||||
return this;
|
return this;
|
||||||
} else if (TDouble.isInfinite(value)) {
|
} else if (Double.isInfinite(value)) {
|
||||||
if (value > 0) {
|
if (value > 0) {
|
||||||
insertSpace(target, target + 8);
|
insertSpace(target, target + 8);
|
||||||
} else {
|
} else {
|
||||||
|
@ -355,7 +366,7 @@ class TAbstractStringBuilder implements TSerializable, TCharSequence {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
DoubleAnalyzer.Result number = Constants.doubleAnalysisResult;
|
var number = Constants.doubleAnalysisResult;
|
||||||
DoubleAnalyzer.analyze(value, number);
|
DoubleAnalyzer.analyze(value, number);
|
||||||
long mantissa = number.mantissa;
|
long mantissa = number.mantissa;
|
||||||
int exp = number.exponent;
|
int exp = number.exponent;
|
||||||
|
@ -374,6 +385,8 @@ class TAbstractStringBuilder implements TSerializable, TCharSequence {
|
||||||
if (zeros > 0) {
|
if (zeros > 0) {
|
||||||
digits -= zeros;
|
digits -= zeros;
|
||||||
}
|
}
|
||||||
|
var leadingZeros = 0;
|
||||||
|
var leadingZero = false;
|
||||||
|
|
||||||
// Handle special case of exponent close to 0
|
// Handle special case of exponent close to 0
|
||||||
if (exp < 7 && exp >= -3) {
|
if (exp < 7 && exp >= -3) {
|
||||||
|
@ -382,8 +395,10 @@ class TAbstractStringBuilder implements TSerializable, TCharSequence {
|
||||||
digits = Math.max(digits, intPart + 1);
|
digits = Math.max(digits, intPart + 1);
|
||||||
exp = 0;
|
exp = 0;
|
||||||
} else {
|
} else {
|
||||||
mantissa /= Constants.longPowersOfTen[-exp];
|
intPart = 0;
|
||||||
digits -= exp;
|
leadingZeros = -exp - 1;
|
||||||
|
leadingZero = true;
|
||||||
|
sz++;
|
||||||
exp = 0;
|
exp = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -405,7 +420,7 @@ class TAbstractStringBuilder implements TSerializable, TCharSequence {
|
||||||
if (exp != 0 && digits == intPart) {
|
if (exp != 0 && digits == intPart) {
|
||||||
digits++;
|
digits++;
|
||||||
}
|
}
|
||||||
sz += digits;
|
sz += digits + leadingZeros;
|
||||||
|
|
||||||
// Print mantissa
|
// Print mantissa
|
||||||
insertSpace(target, target + sz);
|
insertSpace(target, target + sz);
|
||||||
|
@ -413,6 +428,13 @@ class TAbstractStringBuilder implements TSerializable, TCharSequence {
|
||||||
buffer[target++] = '-';
|
buffer[target++] = '-';
|
||||||
}
|
}
|
||||||
long pos = DoubleAnalyzer.DOUBLE_MAX_POS;
|
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) {
|
for (int i = 0; i < digits; ++i) {
|
||||||
int intDigit;
|
int intDigit;
|
||||||
if (pos > 0) {
|
if (pos > 0) {
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
package org.teavm.classlib.java.lang;
|
package org.teavm.classlib.java.lang;
|
||||||
|
|
||||||
import org.teavm.backend.javascript.spi.InjectedBy;
|
import org.teavm.backend.javascript.spi.InjectedBy;
|
||||||
|
import org.teavm.classlib.impl.text.DoubleSynthesizer;
|
||||||
import org.teavm.interop.Import;
|
import org.teavm.interop.Import;
|
||||||
import org.teavm.interop.NoSideEffects;
|
import org.teavm.interop.NoSideEffects;
|
||||||
import org.teavm.interop.Unmanaged;
|
import org.teavm.interop.Unmanaged;
|
||||||
|
@ -25,7 +26,7 @@ import org.teavm.jso.JSBody;
|
||||||
public class TDouble extends TNumber implements TComparable<TDouble> {
|
public class TDouble extends TNumber implements TComparable<TDouble> {
|
||||||
public static final double POSITIVE_INFINITY = 1 / 0.0;
|
public static final double POSITIVE_INFINITY = 1 / 0.0;
|
||||||
public static final double NEGATIVE_INFINITY = -POSITIVE_INFINITY;
|
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 MAX_VALUE = 0x1.FFFFFFFFFFFFFP+1023;
|
||||||
public static final double MIN_NORMAL = -0x1.0P+1022;
|
public static final double MIN_NORMAL = -0x1.0P+1022;
|
||||||
public static final double MIN_VALUE = 0x0.0000000000001P-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;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TDouble(TString value) throws TNumberFormatException {
|
public TDouble(String value) throws TNumberFormatException {
|
||||||
this.value = parseDouble(value);
|
this.value = parseDouble(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,21 +72,21 @@ public class TDouble extends TNumber implements TComparable<TDouble> {
|
||||||
return new TStringBuilder().append(d).toString();
|
return new TStringBuilder().append(d).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TDouble valueOf(TString string) {
|
public static TDouble valueOf(String string) {
|
||||||
return valueOf(parseDouble(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
|
// TODO: parse infinite and different radix
|
||||||
|
|
||||||
if (string.isEmpty()) {
|
if (string.isEmpty()) {
|
||||||
throw new TNumberFormatException();
|
throw new NumberFormatException();
|
||||||
}
|
}
|
||||||
int start = 0;
|
int start = 0;
|
||||||
int end = string.length();
|
int end = string.length();
|
||||||
while (string.charAt(start) <= ' ') {
|
while (string.charAt(start) <= ' ') {
|
||||||
if (++start == end) {
|
if (++start == end) {
|
||||||
throw new TNumberFormatException();
|
throw new NumberFormatException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (string.charAt(end - 1) <= ' ') {
|
while (string.charAt(end - 1) <= ' ') {
|
||||||
|
@ -101,17 +102,18 @@ public class TDouble extends TNumber implements TComparable<TDouble> {
|
||||||
++index;
|
++index;
|
||||||
}
|
}
|
||||||
if (index == end) {
|
if (index == end) {
|
||||||
throw new TNumberFormatException();
|
throw new NumberFormatException();
|
||||||
}
|
}
|
||||||
char c = string.charAt(index);
|
char c = string.charAt(index);
|
||||||
|
|
||||||
long mantissa = 0;
|
long mantissa = 0;
|
||||||
int exp = 0;
|
int exp = -1;
|
||||||
boolean hasOneDigit = false;
|
boolean hasOneDigit = false;
|
||||||
|
long mantissaPos = 1000000000000000000L;
|
||||||
if (c != '.') {
|
if (c != '.') {
|
||||||
hasOneDigit = true;
|
hasOneDigit = true;
|
||||||
if (c < '0' || c > '9') {
|
if (c < '0' || c > '9') {
|
||||||
throw new TNumberFormatException();
|
throw new NumberFormatException();
|
||||||
}
|
}
|
||||||
while (index < end && string.charAt(index) == '0') {
|
while (index < end && string.charAt(index) == '0') {
|
||||||
++index;
|
++index;
|
||||||
|
@ -121,11 +123,11 @@ public class TDouble extends TNumber implements TComparable<TDouble> {
|
||||||
if (c < '0' || c > '9') {
|
if (c < '0' || c > '9') {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (mantissa < TLong.MAX_VALUE / 10 - 9) {
|
if (mantissaPos > 0) {
|
||||||
mantissa = mantissa * 10 + (c - '0');
|
mantissa = mantissa + (mantissaPos * (c - '0'));
|
||||||
} else {
|
mantissaPos = Long.divideUnsigned(mantissaPos, 10);
|
||||||
++exp;
|
|
||||||
}
|
}
|
||||||
|
++exp;
|
||||||
++index;
|
++index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -136,26 +138,28 @@ public class TDouble extends TNumber implements TComparable<TDouble> {
|
||||||
if (c < '0' || c > '9') {
|
if (c < '0' || c > '9') {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (mantissa < TLong.MAX_VALUE / 10 - 9) {
|
if (mantissa == 0 && c == '0') {
|
||||||
mantissa = mantissa * 10 + (c - '0');
|
exp--;
|
||||||
--exp;
|
} else if (mantissaPos > 0) {
|
||||||
|
mantissa = mantissa + (mantissaPos * (c - '0'));
|
||||||
|
mantissaPos = Long.divideUnsigned(mantissaPos, 10);
|
||||||
}
|
}
|
||||||
++index;
|
++index;
|
||||||
hasOneDigit = true;
|
hasOneDigit = true;
|
||||||
}
|
}
|
||||||
if (!hasOneDigit) {
|
if (!hasOneDigit) {
|
||||||
throw new TNumberFormatException();
|
throw new NumberFormatException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (index < end) {
|
if (index < end) {
|
||||||
c = string.charAt(index);
|
c = string.charAt(index);
|
||||||
if (c != 'e' && c != 'E') {
|
if (c != 'e' && c != 'E') {
|
||||||
throw new TNumberFormatException();
|
throw new NumberFormatException();
|
||||||
}
|
}
|
||||||
++index;
|
++index;
|
||||||
boolean negativeExp = false;
|
boolean negativeExp = false;
|
||||||
if (index == end) {
|
if (index == end) {
|
||||||
throw new TNumberFormatException();
|
throw new NumberFormatException();
|
||||||
}
|
}
|
||||||
if (string.charAt(index) == '-') {
|
if (string.charAt(index) == '-') {
|
||||||
++index;
|
++index;
|
||||||
|
@ -175,39 +179,15 @@ public class TDouble extends TNumber implements TComparable<TDouble> {
|
||||||
++index;
|
++index;
|
||||||
}
|
}
|
||||||
if (!hasOneDigit) {
|
if (!hasOneDigit) {
|
||||||
throw new TNumberFormatException();
|
throw new NumberFormatException();
|
||||||
}
|
}
|
||||||
if (negativeExp) {
|
if (negativeExp) {
|
||||||
numExp = -numExp;
|
numExp = -numExp;
|
||||||
}
|
}
|
||||||
exp += 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) {
|
return DoubleSynthesizer.synthesizeDouble(mantissa, exp, negative);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -255,12 +235,6 @@ public class TDouble extends TNumber implements TComparable<TDouble> {
|
||||||
@Unmanaged
|
@Unmanaged
|
||||||
public static native boolean isNaN(double v);
|
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);")
|
@JSBody(params = "v", script = "return !isFinite(v);")
|
||||||
@Import(module = "teavm", name = "isinf")
|
@Import(module = "teavm", name = "isinf")
|
||||||
@NoSideEffects
|
@NoSideEffects
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.classlib.java.lang;
|
package org.teavm.classlib.java.lang;
|
||||||
|
|
||||||
|
import org.teavm.classlib.impl.text.FloatSynthesizer;
|
||||||
import org.teavm.interop.Import;
|
import org.teavm.interop.Import;
|
||||||
import org.teavm.interop.NoSideEffects;
|
import org.teavm.interop.NoSideEffects;
|
||||||
import org.teavm.interop.Unmanaged;
|
import org.teavm.interop.Unmanaged;
|
||||||
|
@ -23,7 +24,7 @@ import org.teavm.jso.JSBody;
|
||||||
public class TFloat extends TNumber implements TComparable<TFloat> {
|
public class TFloat extends TNumber implements TComparable<TFloat> {
|
||||||
public static final float POSITIVE_INFINITY = 1 / 0.0f;
|
public static final float POSITIVE_INFINITY = 1 / 0.0f;
|
||||||
public static final float NEGATIVE_INFINITY = -POSITIVE_INFINITY;
|
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 MAX_VALUE = 0x1.fffffeP+127f;
|
||||||
public static final float MIN_VALUE = 0x1.0p-126f;
|
public static final float MIN_VALUE = 0x1.0p-126f;
|
||||||
public static final float MIN_NORMAL = 0x0.000002P-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);
|
this((float) value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TFloat(TString value) throws TNumberFormatException {
|
public TFloat(String value) throws TNumberFormatException {
|
||||||
this(parseFloat(value));
|
this(parseFloat(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,13 +114,7 @@ public class TFloat extends TNumber implements TComparable<TFloat> {
|
||||||
@Unmanaged
|
@Unmanaged
|
||||||
public static native boolean isFinite(float v);
|
public static native boolean isFinite(float v);
|
||||||
|
|
||||||
@JSBody(script = "return NaN;")
|
public static float parseFloat(String string) throws TNumberFormatException {
|
||||||
@Import(module = "teavm", name = "teavm_getNaN")
|
|
||||||
@NoSideEffects
|
|
||||||
@Unmanaged
|
|
||||||
private static native float getNaN();
|
|
||||||
|
|
||||||
public static float parseFloat(TString string) throws TNumberFormatException {
|
|
||||||
// TODO: parse infinite and different radix
|
// TODO: parse infinite and different radix
|
||||||
|
|
||||||
if (string.isEmpty()) {
|
if (string.isEmpty()) {
|
||||||
|
@ -150,7 +145,8 @@ public class TFloat extends TNumber implements TComparable<TFloat> {
|
||||||
char c = string.charAt(index);
|
char c = string.charAt(index);
|
||||||
|
|
||||||
int mantissa = 0;
|
int mantissa = 0;
|
||||||
int exp = 0;
|
int exp = -1;
|
||||||
|
int mantissaPos = 100000000;
|
||||||
|
|
||||||
boolean hasOneDigit = false;
|
boolean hasOneDigit = false;
|
||||||
if (c != '.') {
|
if (c != '.') {
|
||||||
|
@ -167,11 +163,11 @@ public class TFloat extends TNumber implements TComparable<TFloat> {
|
||||||
if (c < '0' || c > '9') {
|
if (c < '0' || c > '9') {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (mantissa < (TInteger.MAX_VALUE / 10) - 9) {
|
if (mantissaPos > 0) {
|
||||||
mantissa = mantissa * 10 + (c - '0');
|
mantissa = mantissa + (mantissaPos * (c - '0'));
|
||||||
} else {
|
mantissaPos = Integer.divideUnsigned(mantissaPos, 10);
|
||||||
++exp;
|
|
||||||
}
|
}
|
||||||
|
++exp;
|
||||||
++index;
|
++index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -183,9 +179,11 @@ public class TFloat extends TNumber implements TComparable<TFloat> {
|
||||||
if (c < '0' || c > '9') {
|
if (c < '0' || c > '9') {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (mantissa < (TInteger.MAX_VALUE / 10) - 9) {
|
if (mantissa == 0 && c == '0') {
|
||||||
mantissa = mantissa * 10 + (c - '0');
|
exp--;
|
||||||
--exp;
|
} else if (mantissaPos > 0) {
|
||||||
|
mantissa = mantissa + (mantissaPos * (c - '0'));
|
||||||
|
mantissaPos = Integer.divideUnsigned(mantissaPos, 10);
|
||||||
}
|
}
|
||||||
++index;
|
++index;
|
||||||
hasOneDigit = true;
|
hasOneDigit = true;
|
||||||
|
@ -229,35 +227,11 @@ public class TFloat extends TNumber implements TComparable<TFloat> {
|
||||||
}
|
}
|
||||||
exp += numExp;
|
exp += numExp;
|
||||||
}
|
}
|
||||||
if (exp > 38 || exp == 38 && mantissa > 34028234) {
|
|
||||||
return !negative ? POSITIVE_INFINITY : NEGATIVE_INFINITY;
|
return FloatSynthesizer.synthesizeFloat(mantissa, exp, negative);
|
||||||
}
|
|
||||||
if (negative) {
|
|
||||||
mantissa = -mantissa;
|
|
||||||
}
|
|
||||||
return mantissa * decimalExponent(exp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static float decimalExponent(int n) {
|
public static TFloat valueOf(String s) throws TNumberFormatException {
|
||||||
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 {
|
|
||||||
return valueOf(parseFloat(s));
|
return valueOf(parseFloat(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,10 +20,10 @@ import java.math.BigInteger;
|
||||||
import java.text.DecimalFormatSymbols;
|
import java.text.DecimalFormatSymbols;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import org.teavm.classlib.impl.text.DoubleAnalyzer;
|
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.text.FloatAnalyzer;
|
||||||
import org.teavm.classlib.impl.unicode.CLDRHelper;
|
import org.teavm.classlib.impl.unicode.CLDRHelper;
|
||||||
import org.teavm.classlib.java.lang.TArithmeticException;
|
import org.teavm.classlib.java.lang.TArithmeticException;
|
||||||
import org.teavm.classlib.java.lang.TDouble;
|
|
||||||
import org.teavm.classlib.java.util.TLocale;
|
import org.teavm.classlib.java.util.TLocale;
|
||||||
|
|
||||||
public class TDecimalFormat extends TNumberFormat {
|
public class TDecimalFormat extends TNumberFormat {
|
||||||
|
@ -494,15 +494,22 @@ public class TDecimalFormat extends TNumberFormat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expose result
|
|
||||||
if (mantissa == 0 && !positive) {
|
if (mantissa == 0 && !positive) {
|
||||||
return -0.0;
|
return -0.0;
|
||||||
}
|
}
|
||||||
if (exponent == 0) {
|
if (exponent == 0) {
|
||||||
return positive ? mantissa : -mantissa;
|
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
|
@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();
|
extern double teavm_rand();
|
||||||
|
|
||||||
static inline float teavm_getNaN() {
|
|
||||||
return NAN;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern void teavm_beforeInit();
|
extern void teavm_beforeInit();
|
||||||
extern void teavm_afterInitClasses();
|
extern void teavm_afterInitClasses();
|
||||||
|
|
||||||
|
|
|
@ -15,10 +15,6 @@ static int32_t wasm_heap_size;
|
||||||
static int wasm_args;
|
static int wasm_args;
|
||||||
static char** wasm_argv;
|
static char** wasm_argv;
|
||||||
|
|
||||||
float teavm_teavm_getNaN() {
|
|
||||||
return NAN;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define teavmMath_sin sin
|
#define teavmMath_sin sin
|
||||||
#define teavmMath_cos cos
|
#define teavmMath_cos cos
|
||||||
#define teavmMath_sqrt sqrt
|
#define teavmMath_sqrt sqrt
|
||||||
|
|
|
@ -68,6 +68,11 @@ gradle.allprojects {
|
||||||
tasks.withType<Javadoc>().configureEach {
|
tasks.withType<Javadoc>().configureEach {
|
||||||
options.encoding = "UTF-8"
|
options.encoding = "UTF-8"
|
||||||
}
|
}
|
||||||
|
tasks.withType<JavaExec>().configureEach {
|
||||||
|
if (name.endsWith("main()")) {
|
||||||
|
notCompatibleWithConfigurationCache("JavaExec created by IntelliJ")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gradle.afterProject {
|
gradle.afterProject {
|
||||||
|
|
|
@ -18,6 +18,7 @@ package org.teavm.classlib.java.lang;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
import java.util.Random;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.teavm.junit.TeaVMTestRunner;
|
import org.teavm.junit.TeaVMTestRunner;
|
||||||
|
@ -49,11 +50,28 @@ public class DoubleTest {
|
||||||
assertEquals(0, Double.parseDouble("23E-8000"), 1E-12);
|
assertEquals(0, Double.parseDouble("23E-8000"), 1E-12);
|
||||||
assertEquals(0, Double.parseDouble("00000"), 1E-12);
|
assertEquals(0, Double.parseDouble("00000"), 1E-12);
|
||||||
assertEquals(0, Double.parseDouble("00000.0000"), 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(4499999999999888888888888.0, Double.parseDouble("4499999999999888888888888"), 1E9);
|
||||||
assertEquals(0.4499999999999888888888888, Double.parseDouble("0.4499999999999888888888888"), 1E-15);
|
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
|
@Test
|
||||||
public void parsedWithError() {
|
public void parsedWithError() {
|
||||||
checkIllegalFormat("");
|
checkIllegalFormat("");
|
||||||
|
|
|
@ -30,9 +30,11 @@ import org.junit.runner.RunWith;
|
||||||
import org.teavm.junit.TeaVMProperties;
|
import org.teavm.junit.TeaVMProperties;
|
||||||
import org.teavm.junit.TeaVMProperty;
|
import org.teavm.junit.TeaVMProperty;
|
||||||
import org.teavm.junit.TeaVMTestRunner;
|
import org.teavm.junit.TeaVMTestRunner;
|
||||||
|
import org.teavm.junit.WholeClassCompilation;
|
||||||
|
|
||||||
@RunWith(TeaVMTestRunner.class)
|
@RunWith(TeaVMTestRunner.class)
|
||||||
@TeaVMProperties(@TeaVMProperty(key = "java.util.Locale.available", value = "en, en_US, en_GB, ru, ru_RU"))
|
@TeaVMProperties(@TeaVMProperty(key = "java.util.Locale.available", value = "en, en_US, en_GB, ru, ru_RU"))
|
||||||
|
@WholeClassCompilation
|
||||||
public class DecimalFormatTest {
|
public class DecimalFormatTest {
|
||||||
private static DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.ENGLISH);
|
private static DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.ENGLISH);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user