mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
classlib: implement float support for String.format (#873)
This commit is contained in:
parent
bb837bd020
commit
953c475b46
|
@ -245,7 +245,7 @@ public class TDecimalFormat extends TNumberFormat {
|
||||||
if (digit >= 0 && digit <= 9) {
|
if (digit >= 0 && digit <= 9) {
|
||||||
if (!fractionalPart) {
|
if (!fractionalPart) {
|
||||||
++intSize;
|
++intSize;
|
||||||
allowGroupSeparator = groupingSize > 1;
|
allowGroupSeparator = isGroupingUsed() && groupingSize > 1;
|
||||||
} else {
|
} else {
|
||||||
++fracSize;
|
++fracSize;
|
||||||
}
|
}
|
||||||
|
@ -383,7 +383,7 @@ public class TDecimalFormat extends TNumberFormat {
|
||||||
if (digit >= 0 && digit <= 9) {
|
if (digit >= 0 && digit <= 9) {
|
||||||
if (!fractionalPart) {
|
if (!fractionalPart) {
|
||||||
++intSize;
|
++intSize;
|
||||||
allowGroupSeparator = groupingSize > 1;
|
allowGroupSeparator = isGroupingUsed() && groupingSize > 1;
|
||||||
} else {
|
} else {
|
||||||
++fracSize;
|
++fracSize;
|
||||||
}
|
}
|
||||||
|
@ -710,7 +710,7 @@ public class TDecimalFormat extends TNumberFormat {
|
||||||
int digitPos = Math.max(intLength, getMinimumIntegerDigits()) - 1;
|
int digitPos = Math.max(intLength, getMinimumIntegerDigits()) - 1;
|
||||||
for (int i = getMinimumIntegerDigits() - 1; i >= intLength; --i) {
|
for (int i = getMinimumIntegerDigits() - 1; i >= intLength; --i) {
|
||||||
buffer.append('0');
|
buffer.append('0');
|
||||||
if (groupingSize > 0 && digitPos % groupingSize == 0 && digitPos > 0) {
|
if (isGroupingUsed() && groupingSize > 0 && digitPos % groupingSize == 0 && digitPos > 0) {
|
||||||
buffer.append(symbols.getGroupingSeparator());
|
buffer.append(symbols.getGroupingSeparator());
|
||||||
}
|
}
|
||||||
--digitPos;
|
--digitPos;
|
||||||
|
@ -723,7 +723,7 @@ public class TDecimalFormat extends TNumberFormat {
|
||||||
long mantissaDigitMask = POW10_ARRAY[mantissaDigit--];
|
long mantissaDigitMask = POW10_ARRAY[mantissaDigit--];
|
||||||
buffer.append(forDigit(Math.abs((int) (mantissa / mantissaDigitMask))));
|
buffer.append(forDigit(Math.abs((int) (mantissa / mantissaDigitMask))));
|
||||||
mantissa %= mantissaDigitMask;
|
mantissa %= mantissaDigitMask;
|
||||||
if (groupingSize > 0 && digitPos % groupingSize == 0 && digitPos > 0) {
|
if (isGroupingUsed() && groupingSize > 0 && digitPos % groupingSize == 0 && digitPos > 0) {
|
||||||
buffer.append(symbols.getGroupingSeparator());
|
buffer.append(symbols.getGroupingSeparator());
|
||||||
}
|
}
|
||||||
--digitPos;
|
--digitPos;
|
||||||
|
@ -733,7 +733,7 @@ public class TDecimalFormat extends TNumberFormat {
|
||||||
intLength -= significantIntDigits;
|
intLength -= significantIntDigits;
|
||||||
for (int i = 0; i < intLength; ++i) {
|
for (int i = 0; i < intLength; ++i) {
|
||||||
buffer.append('0');
|
buffer.append('0');
|
||||||
if (groupingSize > 0 && digitPos % groupingSize == 0 && digitPos > 0) {
|
if (isGroupingUsed() && groupingSize > 0 && digitPos % groupingSize == 0 && digitPos > 0) {
|
||||||
buffer.append(symbols.getGroupingSeparator());
|
buffer.append(symbols.getGroupingSeparator());
|
||||||
}
|
}
|
||||||
--digitPos;
|
--digitPos;
|
||||||
|
@ -901,7 +901,7 @@ public class TDecimalFormat extends TNumberFormat {
|
||||||
int digitPos = Math.max(intLength, getMinimumIntegerDigits()) - 1;
|
int digitPos = Math.max(intLength, getMinimumIntegerDigits()) - 1;
|
||||||
for (int i = getMinimumIntegerDigits() - 1; i >= intLength; --i) {
|
for (int i = getMinimumIntegerDigits() - 1; i >= intLength; --i) {
|
||||||
buffer.append('0');
|
buffer.append('0');
|
||||||
if (groupingSize > 0 && digitPos % groupingSize == 0 && digitPos > 0) {
|
if (isGroupingUsed() && groupingSize > 0 && digitPos % groupingSize == 0 && digitPos > 0) {
|
||||||
buffer.append(symbols.getGroupingSeparator());
|
buffer.append(symbols.getGroupingSeparator());
|
||||||
}
|
}
|
||||||
--digitPos;
|
--digitPos;
|
||||||
|
@ -914,7 +914,7 @@ public class TDecimalFormat extends TNumberFormat {
|
||||||
BigInteger[] parts = mantissa.divideAndRemainder(mantissaDigitMask);
|
BigInteger[] parts = mantissa.divideAndRemainder(mantissaDigitMask);
|
||||||
buffer.append(forDigit(Math.abs(parts[0].intValue())));
|
buffer.append(forDigit(Math.abs(parts[0].intValue())));
|
||||||
mantissa = parts[1];
|
mantissa = parts[1];
|
||||||
if (groupingSize > 0 && digitPos % groupingSize == 0 && digitPos > 0) {
|
if (isGroupingUsed() && groupingSize > 0 && digitPos % groupingSize == 0 && digitPos > 0) {
|
||||||
buffer.append(symbols.getGroupingSeparator());
|
buffer.append(symbols.getGroupingSeparator());
|
||||||
}
|
}
|
||||||
--digitPos;
|
--digitPos;
|
||||||
|
@ -926,7 +926,7 @@ public class TDecimalFormat extends TNumberFormat {
|
||||||
intLength -= significantIntDigits;
|
intLength -= significantIntDigits;
|
||||||
for (int i = 0; i < intLength; ++i) {
|
for (int i = 0; i < intLength; ++i) {
|
||||||
buffer.append('0');
|
buffer.append('0');
|
||||||
if (groupingSize > 0 && digitPos % groupingSize == 0 && digitPos > 0) {
|
if (isGroupingUsed() && groupingSize > 0 && digitPos % groupingSize == 0 && digitPos > 0) {
|
||||||
buffer.append(symbols.getGroupingSeparator());
|
buffer.append(symbols.getGroupingSeparator());
|
||||||
}
|
}
|
||||||
--digitPos;
|
--digitPos;
|
||||||
|
|
|
@ -22,6 +22,7 @@ import java.io.OutputStream;
|
||||||
import java.io.OutputStreamWriter;
|
import java.io.OutputStreamWriter;
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
import java.text.DecimalFormatSymbols;
|
import java.text.DecimalFormatSymbols;
|
||||||
import java.text.NumberFormat;
|
import java.text.NumberFormat;
|
||||||
|
@ -31,6 +32,7 @@ import java.util.IllegalFormatConversionException;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.UnknownFormatConversionException;
|
import java.util.UnknownFormatConversionException;
|
||||||
import org.teavm.classlib.impl.IntegerUtil;
|
import org.teavm.classlib.impl.IntegerUtil;
|
||||||
|
import org.teavm.classlib.java.text.TDecimalFormat;
|
||||||
|
|
||||||
public final class TFormatter implements Closeable, Flushable {
|
public final class TFormatter implements Closeable, Flushable {
|
||||||
private Locale locale;
|
private Locale locale;
|
||||||
|
@ -240,11 +242,98 @@ public final class TFormatter implements Closeable, Flushable {
|
||||||
formatRadixInt(specifier, 4, true);
|
formatRadixInt(specifier, 4, true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'f':
|
||||||
|
formatFloat(specifier, false);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new UnknownFormatConversionException(String.valueOf(specifier));
|
throw new UnknownFormatConversionException(String.valueOf(specifier));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void formatFloat(char specifier, boolean upperCase) throws IOException {
|
||||||
|
verifyFlags(specifier, MASK_FOR_INT_DECIMAL_FORMAT);
|
||||||
|
verifyFloatFlags();
|
||||||
|
|
||||||
|
if (precision == -1) {
|
||||||
|
precision = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object arg = args[argumentIndex];
|
||||||
|
boolean negative;
|
||||||
|
if (arg instanceof Double) {
|
||||||
|
negative = (Double) arg < 0;
|
||||||
|
} else if (arg instanceof Float) {
|
||||||
|
negative = (Float) arg < 0;
|
||||||
|
} else if (arg instanceof BigDecimal) {
|
||||||
|
negative = ((BigDecimal) arg).signum() < 0;
|
||||||
|
} else {
|
||||||
|
throw new IllegalFormatConversionException(specifier, arg == null ? null : arg.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
TDecimalFormat format = new TDecimalFormat();
|
||||||
|
format.setDecimalFormatSymbols(new DecimalFormatSymbols(locale));
|
||||||
|
if (width != -1) {
|
||||||
|
int decimalSize = predictDecimalSize(negative, format);
|
||||||
|
format.setMaximumIntegerDigits(decimalSize);
|
||||||
|
if ((flags & TFormattableFlags.ZERO_PADDED) != 0) {
|
||||||
|
format.setMinimumIntegerDigits(decimalSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
format.setMaximumFractionDigits(precision);
|
||||||
|
format.setMinimumFractionDigits(precision);
|
||||||
|
format.setGroupingUsed((flags & TFormattableFlags.GROUPING_SEPARATOR) != 0);
|
||||||
|
if ((flags & TFormattableFlags.PARENTHESIZED_NEGATIVE) != 0) {
|
||||||
|
format.setNegativePrefix("(");
|
||||||
|
format.setNegativeSuffix(")");
|
||||||
|
}
|
||||||
|
if ((flags & TFormattableFlags.SIGNED) != 0) {
|
||||||
|
format.setPositivePrefix("+"); // DecimalFormatSymbols has no plus sign
|
||||||
|
} else if ((flags & TFormattableFlags.LEADING_SPACE) != 0) {
|
||||||
|
format.setPositivePrefix(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
String str = format.format(arg);
|
||||||
|
|
||||||
|
precision = -1; // prevent formatGivenString from trimming
|
||||||
|
|
||||||
|
formatGivenString(upperCase, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int predictDecimalSize(boolean negative, TDecimalFormat format) {
|
||||||
|
int decimalSize = width;
|
||||||
|
if (precision > 0) {
|
||||||
|
decimalSize -= precision + 1; // width also includes decimal places. Subtract them!
|
||||||
|
}
|
||||||
|
// signs take up space as well. Also subtract them!
|
||||||
|
if (negative) {
|
||||||
|
if ((flags & TFormattableFlags.PARENTHESIZED_NEGATIVE) != 0) {
|
||||||
|
decimalSize -= 2;
|
||||||
|
} else {
|
||||||
|
decimalSize--;
|
||||||
|
}
|
||||||
|
} else if ((flags & (TFormattableFlags.SIGNED | TFormattableFlags.LEADING_SPACE)) != 0) {
|
||||||
|
decimalSize--;
|
||||||
|
}
|
||||||
|
// the grouping separator also takes up space. You know the drill.
|
||||||
|
if ((flags & TFormattableFlags.GROUPING_SEPARATOR) != 0) {
|
||||||
|
decimalSize -= decimalSize / (format.getGroupingSize() + 1);
|
||||||
|
}
|
||||||
|
return decimalSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void verifyFloatFlags() {
|
||||||
|
if ((flags & TFormattableFlags.SIGNED) != 0 && (flags & TFormattableFlags.LEADING_SPACE) != 0) {
|
||||||
|
throw new TIllegalFormatFlagsException("+ ");
|
||||||
|
}
|
||||||
|
if ((flags & TFormattableFlags.ZERO_PADDED) != 0 && (flags & TFormattableFlags.LEFT_JUSTIFY) != 0) {
|
||||||
|
throw new TIllegalFormatFlagsException("0-");
|
||||||
|
}
|
||||||
|
if ((flags & TFormattableFlags.LEFT_JUSTIFY) != 0 && width < 0) {
|
||||||
|
throw new TMissingFormatWidthException(format.substring(formatSpecifierStart, index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void formatBoolean(char specifier, boolean upperCase) throws IOException {
|
private void formatBoolean(char specifier, boolean upperCase) throws IOException {
|
||||||
verifyFlagsForGeneralFormat(specifier);
|
verifyFlagsForGeneralFormat(specifier);
|
||||||
Object arg = args[argumentIndex];
|
Object arg = args[argumentIndex];
|
||||||
|
|
|
@ -479,6 +479,22 @@ public class DecimalFormatTest {
|
||||||
assertEquals("23.00 RUB", format.format(23));
|
assertEquals("23.00 RUB", format.format(23));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void formatsManual() {
|
||||||
|
DecimalFormat format = new DecimalFormat();
|
||||||
|
format.setDecimalFormatSymbols(new DecimalFormatSymbols(Locale.US));
|
||||||
|
format.setMaximumFractionDigits(6);
|
||||||
|
format.setMinimumFractionDigits(6);
|
||||||
|
format.setGroupingUsed(false);
|
||||||
|
assertEquals("1.200000", format.format((Object) 1.2));
|
||||||
|
assertEquals("12.200000", format.format((Object) 12.2));
|
||||||
|
format.setMaximumFractionDigits(0);
|
||||||
|
format.setMinimumFractionDigits(0);
|
||||||
|
format.setMaximumIntegerDigits(5);
|
||||||
|
format.setMinimumIntegerDigits(5);
|
||||||
|
assertEquals("00002", format.format((Object) 2.0));
|
||||||
|
}
|
||||||
|
|
||||||
private DecimalFormat createFormat(String format) {
|
private DecimalFormat createFormat(String format) {
|
||||||
return new DecimalFormat(format, symbols);
|
return new DecimalFormat(format, symbols);
|
||||||
}
|
}
|
||||||
|
|
|
@ -253,4 +253,30 @@ public class FormatterTest {
|
||||||
assertEquals(2, e.getPrecision());
|
assertEquals(2, e.getPrecision());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void formatsDouble() {
|
||||||
|
assertEquals("1.200000", new Formatter(Locale.US).format("%f", 1.2).toString());
|
||||||
|
assertEquals("12.200000", new Formatter(Locale.US).format("%f", 12.2).toString());
|
||||||
|
|
||||||
|
assertEquals("00002", new Formatter(Locale.US).format("%05.0f", 2.3).toString());
|
||||||
|
assertEquals("-0023", new Formatter(Locale.US).format("%05.0f", -23f).toString());
|
||||||
|
assertEquals("1,234.600000", new Formatter(Locale.US).format("%0,9f", 1234.6).toString());
|
||||||
|
assertEquals("(1,234.6)", new Formatter(Locale.US).format("%0,(9.1f", -1234.6).toString());
|
||||||
|
|
||||||
|
assertEquals("1 12 123 1,234 12,345 123,456 1,234,567", new Formatter(Locale.US)
|
||||||
|
.format("%,.0f %,.0f %,.0f %,.0f %,.0f %,.0f %,.0f", 1f, 12f, 123f, 1234f, 12345f, 123456f, 1234567f)
|
||||||
|
.toString());
|
||||||
|
|
||||||
|
assertEquals(" -123.1:-234.2 ", new Formatter(Locale.US)
|
||||||
|
.format("%7.1f:%-7.1f", -123.1, -234.2).toString());
|
||||||
|
|
||||||
|
assertEquals("+123.1 +123.2 +0.3", new Formatter(Locale.US)
|
||||||
|
.format("%+.1f %+05.1f %+.1f", 123.1, 123.2, 0.3).toString());
|
||||||
|
|
||||||
|
assertEquals(": 123.0:-123.0:", new Formatter(Locale.US)
|
||||||
|
.format(":% .1f:% .1f:", 123f, -123d).toString());
|
||||||
|
|
||||||
|
assertEquals("12.050", new Formatter(Locale.US).format("%4.3f", 12.05).toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user