import junit.framework.TestCase;
public class DoubleFormatTest extends TestCase {
/**
* Преобразует double в String, используя ограничение длины
* результирующей строки. Доступная длина строки используется
* для представления максимально близкого значения.
* Полученная строка может быть использована для конвертирования
* в double с помощью Double.valueof
* иными словами:
* Math.abs(x - Double.valueOf(format(x, n)))
* минимально при любых x и n;
* @param d - число double для преобразования
* @param length - ограничение длины результирующей строки
* @return строка, представляющая число
*/
private String format(double d, int length) {
//реализация
}
public void testFormatLength() {
assertEquals("1.2345", format(1.2345, 6));
assertEquals("1.2346", format(1.23456, 6));
assertEquals("-1.235", format(-1.23456, 6));
assertEquals("123456", format(123456, 6));
assertEquals("1.23E6", format(1234567, 6));
assertEquals("-1.2E6", format(-1234567, 6));
assertEquals("1.2E-4", format(0.0001234567, 6));
assertEquals("-1E-4", format(-0.0001234567, 6));
assertEquals("-1.2E-4", format(-0.0001234567, 7));
}
}
Задача - реализовать метод format.
То, что я нашел в жаве, апаче и остальном гугле, может форматировать числа с ограничением на длину дробной части, а мне нужно ограничивать общую длину, включая символы разделителя, знака, экспоненты, знака экспоненты.
Есть ли готовые решения, или эта проблема только меня заботит?
Решение №1 предоставлено
sassa_nf. Тест пришлось немного подкорректировать, потомучто стандартные вещи делают не -1.2E8 а -1.2E+08, что конечно отнимает лишних два символа, но это всетаки истиный стандартный String.format().
public static String formatSassa_nf(int w, double d) {
String r = String.format("%01." + w + "G", d);
if (r.length() > w) {
r = String.format("%01." + (w + w - r.length()) + "G", d);
}
return r;
}
private String format(double d, int length) {
return formatSassa_nf(length,d);
}
public void testFormatLength() {
assertEquals("1.2345", format(1.2345, 6));
assertEquals("1.2346", format(1.23456, 6));
assertEquals("-1.235", format(-1.23456, 6));
assertEquals("123456", format(123456, 6));
assertEquals("1.23E+08", format(123456789, 8));
assertEquals("-1.2E+08", format(-123456789, 8));
assertEquals("1.2E-05", format(0.00001234567, 7));
assertEquals("-1E-05", format(-0.00001234567, 6));
assertEquals("-1.2E-05", format(-0.00001234567, 8));
}
All Tests Passed.
Решение №2 предоставил
romikk. В сравнении с предыдущим, решение более объемное, но, вместе с тем, более близкое к первоначальным условиям, а значит, более эффективно использует доступные разряды. Первоначальный тест изменился в одном месте, была добавлена точка перед E, но это не противоречит требованиям, т.к. прекрасно обрабатывается методом Double.valueOf(). Еще один момент - это то, что разделитель целой и дробной части для DecimalFormat зависит от локали, но это контролируется, поэтому момент положительный.
private String formatRomikk(double d, int length) {
if (d < 0) length--;
int exponent = (int) Math.log10(Math.abs(d)) + 1;
DecimalFormat format = null;
if (exponent == length) {
format = new DecimalFormat(
String.valueOf(createCharArray(length)));
} else if (exponent > 0) {
if (exponent > length) {
format = new DecimalFormat("0." +
String.valueOf(createCharArray(length - 4)) +
"E0");
} else {
char[] c = createCharArray(length);
c[exponent] = '.';
format = new DecimalFormat(String.valueOf(c));
}
} else {
format = new DecimalFormat("0." +
String.valueOf(createCharArray(length - 5)) +
"E0");
}
return format.format(d);
}
private char[] createCharArray(int length) {
char[] c = new char[length];
Arrays.fill(c, '#');
return c;
}
private String format(double d, int length) {
return formatRomikk(d, length);
}
public void testFormatRomikk() {
Locale.setDefault(Locale.US);
assertEquals("1.2345", format(1.2345, 6));
assertEquals("1.2346", format(1.23456, 6));
assertEquals("-1.235", format(-1.23456, 6));
assertEquals("123456", format(123456, 6));
assertEquals("1.2346E8", format(123456789, 8));
assertEquals("-1.235E8", format(-123456789, 8));
assertEquals("1.23E-5", format(0.00001234567, 7));
assertEquals("-1.E-5", format(-0.00001234567, 6));
assertEquals("-1.23E-5", format(-0.00001234567, 8));
}
All Tests Passed. Всем спасибо.