From e1706f242d8512057ca420179452f8e45375d114 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Thu, 14 Sep 2023 20:55:09 +0200 Subject: [PATCH] classlib: improve accuracy of Double.toString --- .../classlib/impl/text/DoubleAnalyzer.java | 4 +- .../classlib/impl/text/DoubleSynthesizer.java | 51 +++++++++++++++++-- 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/classlib/src/main/java/org/teavm/classlib/impl/text/DoubleAnalyzer.java b/classlib/src/main/java/org/teavm/classlib/impl/text/DoubleAnalyzer.java index b13bed31b..3e52e0c71 100644 --- a/classlib/src/main/java/org/teavm/classlib/impl/text/DoubleAnalyzer.java +++ b/classlib/src/main/java/org/teavm/classlib/impl/text/DoubleAnalyzer.java @@ -47,7 +47,7 @@ public final class DoubleAnalyzer { mantissa |= 1L << 52; } - int decExponent = Arrays.binarySearch(exp10Table, exponent); + int decExponent = Arrays.binarySearch(exp10Table, (short) exponent); if (decExponent < 0) { decExponent = -decExponent; } @@ -810,7 +810,7 @@ public final class DoubleAnalyzer { -1468012460592228864L, }; - private static int[] exp10Table = { + static short[] exp10Table = { -70, -66, -63, diff --git a/classlib/src/main/java/org/teavm/classlib/impl/text/DoubleSynthesizer.java b/classlib/src/main/java/org/teavm/classlib/impl/text/DoubleSynthesizer.java index 5a2cc7b92..71f4dc43d 100644 --- a/classlib/src/main/java/org/teavm/classlib/impl/text/DoubleSynthesizer.java +++ b/classlib/src/main/java/org/teavm/classlib/impl/text/DoubleSynthesizer.java @@ -26,7 +26,7 @@ public final class DoubleSynthesizer { } var binMantissa = DoubleAnalyzer.mulAndShiftRight(mantissa, mantissa10Table[indexInTable], 0); - var binExp = exp10Table[indexInTable]; + int binExp = exp10Table[indexInTable]; var binMantissaShift = (64 - Long.numberOfLeadingZeros(binMantissa)) - 58; if (binMantissaShift >= 0) { @@ -39,7 +39,27 @@ public final class DoubleSynthesizer { if (binExp >= 2047) { return negative ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY; } - binMantissa += 1L << 4; + + var mantissaLowerBits = 5; + var mantissaLowerPos = 1 << mantissaLowerBits; + var error = (int) (binMantissa & (mantissaLowerPos - 1)); + var correction = mantissaLowerPos >> 1; + if (Math.abs(error - (mantissaLowerPos >>> 1)) <= 1) { + var binMantissaWithoutError = binMantissa & -mantissaLowerPos; + var low = calcDecMantissa(binMantissaWithoutError, mantissaLowerPos, indexInTable, binExp); + var hi = calcDecMantissa(binMantissaWithoutError + mantissaLowerPos, mantissaLowerPos, + indexInTable, binExp); + low = mantissa - low; + hi = hi - mantissa; + var cmp = Long.compareUnsigned(low, hi); + if (cmp < 0) { + correction = -error; + } else if (cmp > 0) { + correction = mantissaLowerPos - error; + } + } + + binMantissa += correction; if ((binMantissa & (-1L << 58L)) != 0) { binMantissa >>>= 1; binExp++; @@ -49,7 +69,7 @@ public final class DoubleSynthesizer { binExp = 0; } - binMantissa = (binMantissa >>> 5) & (-1L << 12 >>> 12); + binMantissa = (binMantissa >>> mantissaLowerBits) & (-1L << 12 >>> 12); var iee754 = binMantissa | ((long) binExp << 52); if (negative) { iee754 ^= 1L << 63; @@ -57,6 +77,29 @@ public final class DoubleSynthesizer { return Double.longBitsToDouble(iee754); } + private static long calcDecMantissa(long mantissa, int lowerBit, int indexInTable, int binExp) { + var half = lowerBit >>> 1; + var shift = 7 - (DoubleAnalyzer.exp10Table[indexInTable] - binExp); + var decMantissa = DoubleAnalyzer.mulAndShiftRight(mantissa, + DoubleAnalyzer.mantissa10Table[indexInTable], shift); + var decMantissaHi = DoubleAnalyzer.mulAndShiftRight(mantissa + half, + DoubleAnalyzer.mantissa10Table[indexInTable], shift); + var decMantissaLow = DoubleAnalyzer.mulAndShiftRight(mantissa - half, + DoubleAnalyzer.mantissa10Table[indexInTable], shift); + + var lowerPos = DoubleAnalyzer.findLowerDistance(decMantissa, decMantissaLow); + var upperPos = DoubleAnalyzer.findUpperDistance(decMantissa, decMantissaHi); + var posCmp = Long.compareUnsigned(lowerPos, upperPos); + if (posCmp > 0) { + decMantissa = Long.divideUnsigned(decMantissa, lowerPos) * lowerPos; + } else if (posCmp < 0) { + decMantissa = Long.divideUnsigned(decMantissa, upperPos) * upperPos + upperPos; + } else { + decMantissa = Long.divideUnsigned(decMantissa + (upperPos / 2), upperPos) * upperPos; + } + return decMantissa; + } + // Numbers in the table below are generated by DoubleSynthesizerGenerator private static final long[] mantissa10Table = { @@ -722,7 +765,7 @@ public final class DoubleSynthesizer { -8425902273664687726L, }; - private static int[] exp10Table = { + private static short[] exp10Table = { -76, -72, -69,