Есть ли библиотека форматирования номеров Java, которая обрабатывает значимые цифры?
Встроенный DecimalFormat позволяет вам указывать количество цифр справа от десятичной точки.
Из-за ограничений представления чисел как двойных, форматирование чисел должно включать некоторый уровень округления внутри него. В общем случае это округление должно состоять из нескольких значимых цифр (по умолчанию используется точность двойника), иначе ваш двойной форматирование будет отображаться как 3.5999999 вместо 3.6.
Самое близкое решение, которое я смог найти, это использовать
new BigDecimal(double, new MathContext(14, RoundingMode.HALF_EVEN).stripTrailingZeros().toPlainString()
однако, что обеспечивает только один формат. Если мне нужно отформатировать его вообще (разделитель групп, десятичный разделитель, ограничение числа цифр, заполнение и т.д.), В библиотеке JDK ничего нет.
Существует ли библиотека форматирования общего номера, которая будет обрабатывать округление до значительных цифр правильно?
Кто-то попросил примеров. Итак, допустим, я хотел отформатировать до 4 значащих цифр, а затем:
0.000003599999 -> 0.0000036
4.12345 -> 4.123
1234.345 -> 1234
Общий подход, который мы бы предприняли, состоял бы в округлении до 14 цифр, поскольку в зависимости от обстоятельств двойной может содержать только 15-17 значащих цифр (или так я читал).
Ответы
Ответ 1
Это добавляет много накладных расходов (5 МБ или около того), но в проекте "Международные компоненты для юникода" есть расширенный десятичный формат, который прекрасно красит значимые цифры.
http://icu-project.org/apiref/icu4j/com/ibm/icu/text/DecimalFormat.html#sigdig
DecimalFormat formatter = new DecimalFormat();
formatter.setMaximumSignificantDigits( 4 );
System.out.println( formatter.format( hoursSinceLastAccident ) );
Ответ 2
Стоит отметить, что если он специально ориентирован на Android, а не на Java, встроенный DecimalFormat
поддерживает форматирование значимых цифр с помощью символа @
.
String smallPi = new DecimalFormat("@@@").format("3.14159"); // formats number as '3.14'
Ответ 3
Как насчет такого решения:
double l = 34563.35129854289;
short offset = (short) Math.ceil(Math.log10(l) + 1);
short significantDigits = 10;
System.out.printf("%,." + (significantDigits - offset) + "f", l);
//String output = String.format("%,." + (significantDigits - offset) + "f", l);
Вывод:
34 563,3513 (т.е. ',' в printf указывает на использование разделителя групп)
Ответ 4
Я нашел этот хороший способ сделать это.
Это, если вы просто хотите распечатать его.
public String toSignificantFiguresString(BigDecimal bd, int significantFigures){
return String.format("%."+significantFigures+"G", bd);
}
Это, если вы хотите его преобразовать:
public BigDecimal toSignificantFigures(BigDecimal bd, int significantFigures){
String s = String.format("%."+significantFigures+"G", bd);
BigDecimal result = new BigDecimal(s);
return result;
}
Вот пример этого в действии:
BigDecimal bd2 = toSignificantFigures(BigDecimal.valueOf(4.12345), 4);
BigDecimal bd3 = toSignificantFigures(BigDecimal.valueOf(1234.345), 4);
BigDecimal bd4 = toSignificantFigures(BigDecimal.valueOf(0.000003599999), 4);
System.out.println("bd2: " + String.format("%f",bd2));
System.out.println("bd3: " + String.format("%f",bd3));
System.out.println("bd4: " + String.format("%f",bd4));