Как округлить число до n знаков после запятой в Java - PullRequest
1149 голосов
/ 30 сентября 2008

То, что я хотел бы, - это метод преобразования двойного числа в строку, которая округляется с использованием метода половинного увеличения, т. Е. Если десятичное число округляется до 5, оно всегда округляется до предыдущего числа. Это стандартный метод округления, которого большинство людей ожидают в большинстве ситуаций.

Я также хотел бы, чтобы отображались только значащие цифры, т. Е. Не должно быть никаких завершающих нулей.

Я знаю, что один из способов сделать это - использовать метод String.format:

String.format("%.5g%n", 0.912385);

возвращается:

0.91239

, что здорово, однако оно всегда отображает числа с 5 десятичными разрядами, даже если они не имеют значения:

String.format("%.5g%n", 0.912300);

возвращается:

0.91230

Другой способ - использовать DecimalFormatter:

DecimalFormat df = new DecimalFormat("#.#####");
df.format(0.912385);

возвращается:

0.91238

Однако, как вы можете видеть, используется округление до половины. То есть он округляется вниз, если предыдущая цифра четная. Что я хотел бы это:

0.912385 -> 0.91239
0.912300 -> 0.9123

Каков наилучший способ добиться этого в Java?

Ответы [ 29 ]

18 голосов
/ 27 апреля 2011

Вы можете использовать следующий метод утилиты-

public static double round(double valueToRound, int numberOfDecimalPlaces)
{
    double multipicationFactor = Math.pow(10, numberOfDecimalPlaces);
    double interestedInZeroDPs = valueToRound * multipicationFactor;
    return Math.round(interestedInZeroDPs) / multipicationFactor;
}
16 голосов
/ 15 августа 2015

Вот краткое изложение того, что вы можете использовать, если хотите получить результат в виде строки:

  1. DecimalFormat # setRoundingMode ()

    DecimalFormat df = new DecimalFormat("#.#####");
    df.setRoundingMode(RoundingMode.HALF_UP);
    String str1 = df.format(0.912385)); // 0.91239
    
  2. BigDecimal # setScale ()

    String str2 = new BigDecimal(0.912385)
        .setScale(5, BigDecimal.ROUND_HALF_UP)
        .toString();
    

Вот предложение о том, какие библиотеки вы можете использовать, если вы хотите double в результате. Я бы не рекомендовал это для преобразования строк, так как double может быть не в состоянии точно представить то, что вы хотите (см. Например здесь ):

  1. Точность от Apache Commons Math

    double rounded = Precision.round(0.912385, 5, BigDecimal.ROUND_HALF_UP);
    
  2. Функции от Colt

    double rounded = Functions.round(0.00001).apply(0.912385)
    
  3. Утилиты от Weka

    double rounded = Utils.roundDouble(0.912385, 5)
    
10 голосов
/ 27 июля 2017

Краткое решение:

   public static double round(double value, int precision) {
      int scale = (int) Math.pow(10, precision);
      return (double) Math.round(value * scale) / scale;
  }

См. Также, https://stackoverflow.com/a/22186845/212950 Спасибо jpdymond за предложение.

8 голосов
/ 22 июля 2014

Вы можете использовать BigDecimal

BigDecimal value = new BigDecimal("2.3");
value = value.setScale(0, RoundingMode.UP);
BigDecimal value1 = new BigDecimal("-2.3");
value1 = value1.setScale(0, RoundingMode.UP);
System.out.println(value + "n" + value1);

См .: http://www.javabeat.net/precise-rounding-of-decimals-using-rounding-mode-enumeration/

7 голосов
/ 20 февраля 2014

Попробуйте это: org.apache.commons.math3.util.Precision.round (double x, int scale)

См .: http://commons.apache.org/proper/commons-math/apidocs/org/apache/commons/math3/util/Precision.html

Домашняя страница библиотеки по математике Apache Commons: http://commons.apache.org/proper/commons-math/index.html

Внутренняя реализация этого метода:

public static double round(double x, int scale) {
    return round(x, scale, BigDecimal.ROUND_HALF_UP);
}

public static double round(double x, int scale, int roundingMethod) {
    try {
        return (new BigDecimal
               (Double.toString(x))
               .setScale(scale, roundingMethod))
               .doubleValue();
    } catch (NumberFormatException ex) {
        if (Double.isInfinite(x)) {
            return x;
        } else {
            return Double.NaN;
        }
    }
}
7 голосов
/ 04 июля 2011

Если вам действительно нужны десятичные числа для расчета (и не только для вывода), не используйте двоичный формат с плавающей запятой, такой как double.

Use BigDecimal or any other decimal-based format.

Я использую BigDecimal для расчетов, но имейте в виду, что это зависит от размера номера, с которыми вы имеете дело. В большинстве моих реализаций я нахожу разбор из двойного или от integer до Long достаточно для вычисления очень большого числа.

На самом деле, я имею недавно использовал parsed-to-Long для получения точных представлений (в отличие от шестнадцатеричных результатов) в графическом интерфейсе для чисел, больших как ################################# символов (как пример).

6 голосов
/ 10 ноября 2016

Поскольку я не нашел полного ответа на эту тему, я собрал класс, который должен обрабатывать это правильно, с поддержкой:

  • Форматирование : Простое форматирование двойного до строкового с определенным количеством десятичных знаков
  • Синтаксический анализ : разобрать отформатированное значение до двойного
  • Локаль : форматировать и анализировать, используя локаль по умолчанию
  • Экспоненциальная запись : Начать использовать экспоненциальную запись после определенного порога

Использование довольно просто :

(В этом примере я использую пользовательскую локаль)

public static final int DECIMAL_PLACES = 2;

NumberFormatter formatter = new NumberFormatter(DECIMAL_PLACES);

String value = formatter.format(9.319); // "9,32"
String value2 = formatter.format(0.0000005); // "5,00E-7"
String value3 = formatter.format(1324134123); // "1,32E9"

double parsedValue1 = formatter.parse("0,4E-2", 0); // 0.004
double parsedValue2 = formatter.parse("0,002", 0); // 0.002
double parsedValue3 = formatter.parse("3423,12345", 0); // 3423.12345

Вот класс :

import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.ParseException;
import java.util.Locale;

public class NumberFormatter {

    private static final String SYMBOL_INFINITE           = "\u221e";
    private static final char   SYMBOL_MINUS              = '-';
    private static final char   SYMBOL_ZERO               = '0';
    private static final int    DECIMAL_LEADING_GROUPS    = 10;
    private static final int    EXPONENTIAL_INT_THRESHOLD = 1000000000; // After this value switch to exponential notation
    private static final double EXPONENTIAL_DEC_THRESHOLD = 0.0001; // Below this value switch to exponential notation

    private DecimalFormat decimalFormat;
    private DecimalFormat decimalFormatLong;
    private DecimalFormat exponentialFormat;

    private char groupSeparator;

    public NumberFormatter(int decimalPlaces) {
        configureDecimalPlaces(decimalPlaces);
    }

    public void configureDecimalPlaces(int decimalPlaces) {
        if (decimalPlaces <= 0) {
            throw new IllegalArgumentException("Invalid decimal places");
        }

        DecimalFormatSymbols separators = new DecimalFormatSymbols(Locale.getDefault());
        separators.setMinusSign(SYMBOL_MINUS);
        separators.setZeroDigit(SYMBOL_ZERO);

        groupSeparator = separators.getGroupingSeparator();

        StringBuilder decimal = new StringBuilder();
        StringBuilder exponential = new StringBuilder("0.");

        for (int i = 0; i < DECIMAL_LEADING_GROUPS; i++) {
            decimal.append("###").append(i == DECIMAL_LEADING_GROUPS - 1 ? "." : ",");
        }

        for (int i = 0; i < decimalPlaces; i++) {
            decimal.append("#");
            exponential.append("0");
        }

        exponential.append("E0");

        decimalFormat = new DecimalFormat(decimal.toString(), separators);
        decimalFormatLong = new DecimalFormat(decimal.append("####").toString(), separators);
        exponentialFormat = new DecimalFormat(exponential.toString(), separators);

        decimalFormat.setRoundingMode(RoundingMode.HALF_UP);
        decimalFormatLong.setRoundingMode(RoundingMode.HALF_UP);
        exponentialFormat.setRoundingMode(RoundingMode.HALF_UP);
    }

    public String format(double value) {
        String result;
        if (Double.isNaN(value)) {
            result = "";
        } else if (Double.isInfinite(value)) {
            result = String.valueOf(SYMBOL_INFINITE);
        } else {
            double absValue = Math.abs(value);
            if (absValue >= 1) {
                if (absValue >= EXPONENTIAL_INT_THRESHOLD) {
                    value = Math.floor(value);
                    result = exponentialFormat.format(value);
                } else {
                    result = decimalFormat.format(value);
                }
            } else if (absValue < 1 && absValue > 0) {
                if (absValue >= EXPONENTIAL_DEC_THRESHOLD) {
                    result = decimalFormat.format(value);
                    if (result.equalsIgnoreCase("0")) {
                        result = decimalFormatLong.format(value);
                    }
                } else {
                    result = exponentialFormat.format(value);
                }
            } else {
                result = "0";
            }
        }
        return result;
    }

    public String formatWithoutGroupSeparators(double value) {
        return removeGroupSeparators(format(value));
    }

    public double parse(String value, double defValue) {
        try {
            return decimalFormat.parse(value).doubleValue();
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return defValue;
    }

    private String removeGroupSeparators(String number) {
        return number.replace(String.valueOf(groupSeparator), "");
    }

}
5 голосов
/ 03 марта 2016

На всякий случай, если кому-то все еще нужна помощь в этом. Это решение отлично работает для меня.

private String withNoTrailingZeros(final double value, final int nrOfDecimals) {
return new BigDecimal(String.valueOf(value)).setScale(nrOfDecimals,  BigDecimal.ROUND_HALF_UP).stripTrailingZeros().toPlainString();

}

возвращает String с желаемым выводом.

4 голосов
/ 08 сентября 2011

Фрагмент кода ниже показывает, как отображать n цифр. Хитрость заключается в том, чтобы установить переменную pp в 1, за которой следуют n нулей. В приведенном ниже примере значение переменной pp имеет 5 нулей, поэтому будут отображаться 5 цифр.

double pp = 10000;

double myVal = 22.268699999999967;
String needVal = "22.2687";

double i = (5.0/pp);

String format = "%10.4f";
String getVal = String.format(format,(Math.round((myVal +i)*pp)/pp)-i).trim();
4 голосов
/ 28 июня 2015

Если вы используете DecimalFormat для преобразования double в String, это очень просто:

DecimalFormat formatter = new DecimalFormat("0.0##");
formatter.setRoundingMode(RoundingMode.HALF_UP);

double num = 1.234567;
return formatter.format(num);

Существует несколько значений перечисления RoundingMode, в зависимости от требуемого поведения.

...