From 062d4ae4e9d2c9a170e43df142cae9c636088a00 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Fri, 15 Sep 2023 12:11:37 +0200 Subject: [PATCH] classlib: improve accuracy of float parsing and formatting --- .../classlib/impl/text/FloatAnalyzer.java | 46 +- .../classlib/impl/text/FloatSynthesizer.java | 420 +++++++++--------- .../impl/text/FloatSynthesizerGenerator.java | 37 +- .../org/teavm/classlib/java/lang/TFloat.java | 18 +- 4 files changed, 264 insertions(+), 257 deletions(-) diff --git a/classlib/src/main/java/org/teavm/classlib/impl/text/FloatAnalyzer.java b/classlib/src/main/java/org/teavm/classlib/impl/text/FloatAnalyzer.java index 8ba54a352..dae067809 100644 --- a/classlib/src/main/java/org/teavm/classlib/impl/text/FloatAnalyzer.java +++ b/classlib/src/main/java/org/teavm/classlib/impl/text/FloatAnalyzer.java @@ -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)); } diff --git a/classlib/src/main/java/org/teavm/classlib/impl/text/FloatSynthesizer.java b/classlib/src/main/java/org/teavm/classlib/impl/text/FloatSynthesizer.java index 36c8ae14b..5471153bf 100644 --- a/classlib/src/main/java/org/teavm/classlib/impl/text/FloatSynthesizer.java +++ b/classlib/src/main/java/org/teavm/classlib/impl/text/FloatSynthesizer.java @@ -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, }; } diff --git a/classlib/src/main/java/org/teavm/classlib/impl/text/FloatSynthesizerGenerator.java b/classlib/src/main/java/org/teavm/classlib/impl/text/FloatSynthesizerGenerator.java index bf1d5a652..c274788cc 100644 --- a/classlib/src/main/java/org/teavm/classlib/impl/text/FloatSynthesizerGenerator.java +++ b/classlib/src/main/java/org/teavm/classlib/impl/text/FloatSynthesizerGenerator.java @@ -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(); + } } diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TFloat.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TFloat.java index bb65b20e8..8ff8c96c3 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TFloat.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TFloat.java @@ -114,17 +114,17 @@ public class TFloat extends TNumber implements TComparable { @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 { ++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 { 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 { 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 { ++index; } if (!hasOneDigit) { - throw new TNumberFormatException(); + throw new NumberFormatException(); } if (negativeExp) { numExp = -numExp;