Форматирование double

Mar 17, 2010 14:19


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. Всем спасибо.
Previous post Next post
Up