classlib: improve accuracy of float parsing and formatting

This commit is contained in:
Alexey Andreev 2023-09-15 12:11:37 +02:00
parent e1706f242d
commit 062d4ae4e9
4 changed files with 264 additions and 257 deletions

View File

@ -21,6 +21,7 @@ public final class FloatAnalyzer {
public static final int PRECISION = 9;
public static final int MAX_POS = 100000000;
static final int MAX_ABS_DEC_EXP = 50;
private static final int MAX_MANTISSA = Integer.divideUnsigned(-1, 10);
private FloatAnalyzer() {
}
@ -54,13 +55,11 @@ public final class FloatAnalyzer {
int mantissaShift = 9 + binExponentCorrection;
int decMantissa = mulAndShiftRight(mantissa, mantissa10Table[decExponent + 1], mantissaShift);
if (decMantissa >= 1000000000) {
++decExponent;
binExponentCorrection = exponent - exp10Table[decExponent + 1];
mantissaShift = 9 + binExponentCorrection;
decMantissa = mulAndShiftRight(mantissa, mantissa10Table[decExponent + 1], mantissaShift);
} else if (decMantissa < 100000000) {
--decExponent;
if (decMantissa < MAX_MANTISSA) {
while (Integer.compareUnsigned(decMantissa, MAX_MANTISSA) <= 0) {
--decExponent;
decMantissa = decMantissa * 10 + 9;
}
binExponentCorrection = exponent - exp10Table[decExponent + 1];
mantissaShift = 9 + binExponentCorrection;
decMantissa = mulAndShiftRight(mantissa, mantissa10Table[decExponent + 1], mantissaShift);
@ -72,18 +71,21 @@ public final class FloatAnalyzer {
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;
var posCmp = Integer.compareUnsigned(lowerPos, upperPos);
if (posCmp > 0) {
decMantissa = Integer.divideUnsigned(decMantissa, lowerPos) * lowerPos;
} else if (posCmp < 0) {
decMantissa = Integer.divideUnsigned(decMantissa, upperPos) * upperPos + upperPos;
} else {
decMantissa = ((decMantissa + (upperPos / 2)) / upperPos) * upperPos;
decMantissa = Integer.divideUnsigned(decMantissa + (upperPos / 2), upperPos) * upperPos;
}
if (decMantissa >= 1000000000) {
decExponent++;
decMantissa /= 10;
} else if (decMantissa < 100000000) {
if (Long.compareUnsigned(decMantissa, 1000000000) >= 0) {
do {
decExponent++;
decMantissa = Integer.divideUnsigned(decMantissa, 10);
} while (Integer.compareUnsigned(decMantissa, 1000000000) >= 0);
} else if (Integer.compareUnsigned(decMantissa, 100000000) < 0) {
decExponent--;
decMantissa *= 10;
}
@ -94,7 +96,9 @@ public final class FloatAnalyzer {
private static int findLowerDistance(int mantissa, int lower) {
int pos = 1;
while (mantissa / (pos * 10) > lower / (pos * 10)) {
while (Integer.compareUnsigned(
Integer.divideUnsigned(mantissa, pos * 10),
Integer.divideUnsigned(lower, pos * 10)) > 0) {
pos *= 10;
}
return pos;
@ -102,7 +106,9 @@ public final class FloatAnalyzer {
private static int findUpperDistance(int mantissa, int upper) {
int pos = 1;
while (mantissa / (pos * 10) < upper / (pos * 10)) {
while (Integer.compareUnsigned(
Integer.divideUnsigned(mantissa, pos * 10),
Integer.divideUnsigned(upper, pos * 10)) < 0) {
pos *= 10;
}
return pos;
@ -110,10 +116,6 @@ public final class FloatAnalyzer {
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));
}

View File

@ -20,26 +20,30 @@ public final class FloatSynthesizer {
}
public static float synthesizeFloat(int mantissa, int exp, boolean negative) {
var indexInTable = FloatAnalyzer.MAX_ABS_DEC_EXP - exp;
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++;
var binMantissaShift = (32 - Integer.numberOfLeadingZeros(binMantissa)) - 30;
if (binMantissaShift >= 0) {
binMantissa >>>= binMantissaShift;
} else {
binMantissa <<= -binMantissaShift;
}
while (binMantissa < (1L << 29)) {
binMantissa <<= 1;
binExp--;
}
binExp += 5;
binExp += binMantissaShift;
if (binExp >= 255) {
return negative ? Float.NEGATIVE_INFINITY : Float.POSITIVE_INFINITY;
}
binMantissa += 1 << 5;
if ((binMantissa & (-1 << 30)) != 0) {
binMantissa >>>= 1;
binExp++;
}
if (binExp <= 0) {
binMantissa >>= Math.min(-binExp + 1, 32);
binExp = 0;
@ -54,208 +58,208 @@ public final class FloatSynthesizer {
}
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,
-1598972629,
-924973963,
-82475629,
-1662160004,
-1003958181,
-181205903,
-1723866425,
-1081091207,
-277622185,
-1784126602,
-1156416428,
-371778711,
-1842974431,
-1229976214,
-463728444,
-1900443013,
-1301811943,
-553523104,
-1956564676,
-1371964021,
-641213203,
-2011370988,
-1440471911,
-726848064,
-2064892776,
-1507374146,
-810475859,
-2117160148,
-1572708361,
-892143627,
-41437709,
-1636511304,
-971897307,
-141129809,
-1698818867,
-1049781759,
-238485375,
-1759666096,
-1125840795,
-333559170,
-1819087217,
-1200117198,
-426404673,
-1877115657,
-1272652747,
-517074110,
-1933784055,
-1343488244,
-605618481,
-1989124287,
-1412663534,
-692087594,
-2043167482,
-1480217529,
-776530087,
-2095944040,
-1546188227,
-858993459,
-2147483648,
-1610612736,
-939524096,
-100663296,
-1673527296,
-1018167296,
-198967296,
-1734967296,
-1094967296,
-294967296,
-1794967296,
-1169967296,
-388717296,
-1853561046,
-1243209483,
-480270030,
-1910781505,
-1314735057,
-569676998,
-1966660859,
-1384584250,
-656988489,
-2021230542,
-1452796353,
-742253617,
-2074521247,
-1519409734,
-825520344,
-2126562951,
-1584461865,
-906835507,
-59802560,
-1647989336,
-986244846,
-159064233,
-1710027882,
-1063793028,
-255999461,
-1770612399,
-1139523675,
-350662770,
-1829776967,
};
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,
-35,
-32,
-29,
-25,
-22,
-19,
-15,
-12,
-9,
-5,
-2,
1,
5,
8,
11,
15,
18,
21,
25,
28,
31,
35,
38,
41,
45,
48,
51,
55,
58,
61,
64,
68,
71,
74,
78,
81,
84,
88,
91,
94,
98,
101,
104,
108,
111,
114,
118,
121,
124,
128,
131,
134,
138,
141,
144,
148,
151,
154,
158,
161,
164,
167,
171,
174,
177,
181,
184,
187,
191,
194,
197,
201,
204,
207,
211,
214,
217,
221,
224,
227,
231,
234,
237,
241,
244,
247,
251,
254,
257,
260,
264,
267,
270,
274,
277,
280,
284,
287,
290,
294,
};
}

View File

@ -24,29 +24,26 @@ public final class FloatSynthesizerGenerator {
public static void main(String[] args) {
var mantissaList = new int[100];
var expList = new int[100];
var shift = 57;
var exp = 0;
var binOneShift = 256;
var binOne = BigInteger.ONE.shiftLeft(binOneShift);
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();
for (var i = 0; i <= 50; ++i) {
var quot = binOne.divide(dec);
mantissaList[50 - i] = extractInt(quot);
var exp = quot.bitLength() - binOneShift + 30;
expList[50 - i] = 127 + exp;
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;
dec = BigInteger.valueOf(100000000);
var q = BigInteger.TEN;
for (var i = 1; i < 50; ++i) {
var quot = q.shiftLeft(binOneShift).divide(dec);
mantissaList[50 + i] = extractInt(quot);
var exp = quot.bitLength() - binOneShift + 30;
expList[50 + i] = 127 + exp;
q = q.multiply(BigInteger.TEN);
}
System.out.println("[mantissa]");
@ -60,4 +57,8 @@ public final class FloatSynthesizerGenerator {
System.out.println(value + ",");
}
}
private static int extractInt(BigInteger n) {
return n.shiftRight(n.bitLength() - 33).add(BigInteger.ONE).shiftRight(1).intValue();
}
}

View File

@ -114,17 +114,17 @@ public class TFloat extends TNumber implements TComparable<TFloat> {
@Unmanaged
public static native boolean isFinite(float v);
public static float parseFloat(String string) throws TNumberFormatException {
public static float parseFloat(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) <= ' ') {
@ -140,7 +140,7 @@ public class TFloat extends TNumber implements TComparable<TFloat> {
++index;
}
if (index == end) {
throw new TNumberFormatException();
throw new NumberFormatException();
}
char c = string.charAt(index);
@ -152,7 +152,7 @@ public class TFloat extends TNumber implements TComparable<TFloat> {
if (c != '.') {
hasOneDigit = true;
if (c < '0' || c > '9') {
throw new TNumberFormatException();
throw new NumberFormatException();
}
while (index < end && string.charAt(index) == '0') {
@ -189,18 +189,18 @@ public class TFloat extends TNumber implements TComparable<TFloat> {
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;
@ -220,7 +220,7 @@ public class TFloat extends TNumber implements TComparable<TFloat> {
++index;
}
if (!hasOneDigit) {
throw new TNumberFormatException();
throw new NumberFormatException();
}
if (negativeExp) {
numExp = -numExp;