classlib: improve accuracy of Double.toString

This commit is contained in:
Alexey Andreev 2023-09-13 21:20:18 +02:00
parent e2ee9f1dbb
commit b2aae0634e

View File

@ -21,6 +21,7 @@ public final class DoubleAnalyzer {
static final int MAX_ABS_DEC_EXP = 330; static final int MAX_ABS_DEC_EXP = 330;
public static final int DECIMAL_PRECISION = 18; public static final int DECIMAL_PRECISION = 18;
public static final long DOUBLE_MAX_POS = 100000000000000000L; public static final long DOUBLE_MAX_POS = 100000000000000000L;
private static final long MAX_MANTISSA = Long.divideUnsigned(-1, 10);
private DoubleAnalyzer() { private DoubleAnalyzer() {
} }
@ -54,13 +55,11 @@ public final class DoubleAnalyzer {
int mantissaShift = 12 + binExponentCorrection; int mantissaShift = 12 + binExponentCorrection;
long decMantissa = mulAndShiftRight(mantissa, mantissa10Table[decExponent + 1], mantissaShift); long decMantissa = mulAndShiftRight(mantissa, mantissa10Table[decExponent + 1], mantissaShift);
if (decMantissa >= 1000000000000000000L) { if (decMantissa <= MAX_MANTISSA) {
++decExponent; while (Long.compareUnsigned(decMantissa, MAX_MANTISSA) <= 0) {
binExponentCorrection = exponent - exp10Table[decExponent + 1]; --decExponent;
mantissaShift = 12 + binExponentCorrection; decMantissa = decMantissa * 10 + 9;
decMantissa = mulAndShiftRight(mantissa, mantissa10Table[decExponent + 1], mantissaShift); }
} else if (decMantissa <= 100000000000000000L) {
--decExponent;
binExponentCorrection = exponent - exp10Table[decExponent + 1]; binExponentCorrection = exponent - exp10Table[decExponent + 1];
mantissaShift = 12 + binExponentCorrection; mantissaShift = 12 + binExponentCorrection;
decMantissa = mulAndShiftRight(mantissa, mantissa10Table[decExponent + 1], mantissaShift); decMantissa = mulAndShiftRight(mantissa, mantissa10Table[decExponent + 1], mantissaShift);
@ -72,18 +71,21 @@ public final class DoubleAnalyzer {
var lowerPos = findLowerDistance(decMantissa, decMantissaLow); var lowerPos = findLowerDistance(decMantissa, decMantissaLow);
var upperPos = findUpperDistance(decMantissa, decMantissaHi); var upperPos = findUpperDistance(decMantissa, decMantissaHi);
if (lowerPos > upperPos) { var posCmp = Long.compareUnsigned(lowerPos, upperPos);
decMantissa = (decMantissa / lowerPos) * lowerPos; if (posCmp > 0) {
} else if (lowerPos < upperPos) { decMantissa = Long.divideUnsigned(decMantissa, lowerPos) * lowerPos;
decMantissa = (decMantissa / upperPos) * upperPos + upperPos; } else if (posCmp < 0) {
decMantissa = Long.divideUnsigned(decMantissa, upperPos) * upperPos + upperPos;
} else { } else {
decMantissa = ((decMantissa + (upperPos / 2)) / upperPos) * upperPos; decMantissa = Long.divideUnsigned(decMantissa + (upperPos / 2), upperPos) * upperPos;
} }
if (decMantissa >= 1000000000000000000L) { if (Long.compareUnsigned(decMantissa, 1000000000000000000L) >= 0) {
decExponent++; do {
decMantissa /= 10; decExponent++;
} else if (decMantissa < 100000000000000000L) { decMantissa = Long.divideUnsigned(decMantissa, 10);
} while (Long.compareUnsigned(decMantissa, 1000000000000000000L) >= 0);
} else if (Long.compareUnsigned(decMantissa, 100000000000000000L) < 0) {
decExponent--; decExponent--;
decMantissa *= 10; decMantissa *= 10;
} }
@ -94,7 +96,9 @@ public final class DoubleAnalyzer {
private static long findLowerDistance(long mantissa, long lower) { private static long findLowerDistance(long mantissa, long lower) {
long pos = 1; long pos = 1;
while (mantissa / (pos * 10) > lower / (pos * 10)) { while (Long.compareUnsigned(
Long.divideUnsigned(mantissa, pos * 10),
Long.divideUnsigned(lower, pos * 10)) > 0) {
pos *= 10; pos *= 10;
} }
return pos; return pos;
@ -102,7 +106,9 @@ public final class DoubleAnalyzer {
private static long findUpperDistance(long mantissa, long upper) { private static long findUpperDistance(long mantissa, long upper) {
long pos = 1; long pos = 1;
while (mantissa / (pos * 10) < upper / (pos * 10)) { while (Long.compareUnsigned(
Long.divideUnsigned(mantissa, pos * 10),
Long.divideUnsigned(upper, pos * 10)) < 0) {
pos *= 10; pos *= 10;
} }
return pos; return pos;
@ -128,6 +134,7 @@ public final class DoubleAnalyzer {
long c = (c3 << (32 + shift)) + (c2 << (16 + shift)) + (c1 << shift); long c = (c3 << (32 + shift)) + (c2 << (16 + shift)) + (c1 << shift);
cm += c0 << 16; cm += c0 << 16;
// TODO: removing this gives better result in random tests, however toString(1.0) gives '0.5'
if (((cm >>> (31 - shift)) & 1) != 0) { if (((cm >>> (31 - shift)) & 1) != 0) {
++c; ++c;
} }