Есть ли библиотека форматирования номеров 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));