Как округлить число до n десятичных знаков в Java


то, что я хотел бы,-это метод преобразования double в строку, которая округляется с использованием метода half - up, т. е. если десятичное число округляется до 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 1048

29 ответов:

использовать setRoundingMode, выберите RoundingMode явно для обработки вашей проблемы с полу-четным раундом, а затем используйте шаблон формата для требуемого вывода.

пример:

DecimalFormat df = new DecimalFormat("#.####");
df.setRoundingMode(RoundingMode.CEILING);
for (Number n : Arrays.asList(12, 123.12345, 0.23, 0.1, 2341234.212431324)) {
    Double d = n.doubleValue();
    System.out.println(df.format(d));
}

дает на выходе:

12
123.1235
0.23
0.1
2341234.2125

предполагая, что value это double, вы можете сделать:

(double)Math.round(value * 100000d) / 100000d

Это для точности 5 цифр. Количество нулей указывает на количество десятичных знаков.

new BigDecimal(String.valueOf(double)).setScale(yourScale, BigDecimal.ROUND_HALF_UP);

будет Вам BigDecimal. Чтобы получить строку из него, просто назовите это BigDecimal ' s toString метод, или toPlainString метод для Java 5+ для простой строки формата.

пример программы:

package trials;
import java.math.BigDecimal;

public class Trials {

    public static void main(String[] args) {
        int yourScale = 10;
        System.out.println(BigDecimal.valueOf(0.42344534534553453453-0.42324534524553453453).setScale(yourScale, BigDecimal.ROUND_HALF_UP));
    }

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

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

чтобы убедиться, что у вас есть конечные 0.

как отметили некоторые другие, правильный ответ-использовать либо DecimalFormat или BigDecimal. С плавающей запятой не есть десятичные знаки, поэтому вы не можете округлить / усечь до определенного количества из них в первую очередь. Вы должны работать в десятичном корне, и это то, что делают эти два класса.

я публикую следующий код в качестве встречного примера для всех ответов в этой теме и действительно по всему StackOverflow (и в других местах), которые рекомендуют умножение с последующим усечением с последующим делением. Сторонники этого метода должны объяснить, почему следующий код дает неверный результат более чем в 92% случаев.

public class RoundingCounterExample
{

    static float roundOff(float x, int position)
    {
        float a = x;
        double temp = Math.pow(10.0, position);
        a *= temp;
        a = Math.round(a);
        return (a / (float)temp);
    }

    public static void main(String[] args)
    {
        float a = roundOff(0.0009434f,3);
        System.out.println("a="+a+" (a % .001)="+(a % 0.001));
        int count = 0, errors = 0;
        for (double x = 0.0; x < 1; x += 0.0001)
        {
            count++;
            double d = x;
            int scale = 2;
            double factor = Math.pow(10, scale);
            d = Math.round(d * factor) / factor;
            if ((d % 0.01) != 0.0)
            {
                System.out.println(d + " " + (d % 0.01));
                errors++;
            }
        }
        System.out.println(count + " trials " + errors + " errors");
    }
}

вывод этой программы:

10001 trials 9251 errors

EDIT: чтобы обратиться к некоторым комментариям ниже, я переделал модульную часть тестового цикла с помощью BigDecimal и new MathContext(16) для работы модуля выглядит следующим образом:

public static void main(String[] args)
{
    int count = 0, errors = 0;
    int scale = 2;
    double factor = Math.pow(10, scale);
    MathContext mc = new MathContext(16, RoundingMode.DOWN);
    for (double x = 0.0; x < 1; x += 0.0001)
    {
        count++;
        double d = x;
        d = Math.round(d * factor) / factor;
        BigDecimal bd = new BigDecimal(d, mc);
        bd = bd.remainder(new BigDecimal("0.01"), mc);
        if (bd.multiply(BigDecimal.valueOf(100)).remainder(BigDecimal.ONE, mc).compareTo(BigDecimal.ZERO) != 0)
        {
            System.out.println(d + " " + bd);
            errors++;
        }
    }
    System.out.println(count + " trials " + errors + " errors");
}

результат:

10001 trials 4401 errors

предположим, что у вас есть

double d = 9232.129394d;

можно использовать BigDecimal

BigDecimal bd = new BigDecimal(d).setScale(2, RoundingMode.HALF_EVEN);
d = bd.doubleValue();

или без BigDecimal

d = Math.round(d*100)/100.0d;

С обоими решениями d == 9232.13

вы можете использовать класс DecimalFormat.

double d = 3.76628729;

DecimalFormat newFormat = new DecimalFormat("#.##");
double twoDecimal =  Double.valueOf(newFormat.format(d));

реальный Java How-to посты это решение, которое также совместимо для версий до Java 1.6.

BigDecimal bd = new BigDecimal(Double.toString(d));
bd = bd.setScale(decimalPlace, BigDecimal.ROUND_HALF_UP);
return bd.doubleValue();
double myNum = .912385;
int precision = 10000; //keep 4 digits
myNum= Math.floor(myNum * precision +.5)/precision;

@Milhous: десятичный формат для округления отлично:

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

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

чтобы убедиться, что у вас есть конечные 0.

Я бы добавил, что этот метод очень хорошо обеспечивает фактическое числовой, округляющий механизм-не только визуально, но и при обработке.

гипотетический: вы должны реализовать механизм округления в GUI программа. Чтобы изменить точность / точность результата выход просто измените формат курсора (т. е. в скобках). Так что:

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

вернется в качестве вывода: 0.912385

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

вернется в качестве вывода: 0.91239

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

вернется в качестве вывода: 0.9124

[EDIT: также, если формат каретки такой ("#0.############ ") и ты введите десятичное число, например 3.1415926, для аргумента DecimalFormat не производит никакого мусора (например, конечные нули) и вернется: 3.1415926 .. если ты к этому склонен. Конечно, это немного многословно на вкус некоторых разработчиков - но эй, у него низкий объем памяти во время обработки и очень легко реализовать.]

по сути, красота DecimalFormat заключается в том, что он одновременно обрабатывает строку внешний вид-а также уровень точности округления набора. Эрго: ты получите два преимущества по цене одного кода. ;)

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

public static double round(double valueToRound, int numberOfDecimalPlaces)
{
    double multipicationFactor = Math.pow(10, numberOfDecimalPlaces);
    double interestedInZeroDPs = valueToRound * multipicationFactor;
    return Math.round(interestedInZeroDPs) / multipicationFactor;
}

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

  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. функции от Кольта

    double rounded = Functions.round(0.00001).apply(0.912385)
    
  3. Utils от Weka

    double rounded = Utils.roundDouble(0.912385, 5)
    

лаконичный решение:

   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 за предложение этого.

вы можете использовать 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/

попробуйте это: org.апаш.палата общин.math3.утиль.Точность.круглый (двойной x, int масштаб)

см.: 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;
        }
    }
}

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

  • форматирование: легко форматировать двойную строку с определенным количеством десятичных знаков
  • извлечение: разбор отформатированного значения обратно в double
  • Locale: форматирование и анализ с использованием локали по умолчанию
  • экспоненциальная нотация: начать использовать экспоненциальная нотация после определенного порога

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

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

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), "");
    }

}

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

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

}

возвращает a String с желаемым результатом.

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

Use BigDecimal or any other decimal-based format.

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

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

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

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

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

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

в приведенном ниже фрагменте кода показано, как отобразить 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();

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

как округлить число в Java

наиболее распространенным случаем является использование Math.round().

Math.round(3.7) // 4

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

круглые

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

Math.round(3.0); // 3
Math.round(3.1); // 3
Math.round(3.5); // 4
Math.round(3.9); // 4

Math.round(-3.0); // -3
Math.round(-3.1); // -3
Math.round(-3.5); // -3 *** careful here ***
Math.round(-3.9); // -4

ceil

любое десятичное значение округляется до следующего целого числа. Он идет к ceiling. Этот метод возвращает значение double.

Math.ceil(3.0); // 3.0
Math.ceil(3.1); // 4.0
Math.ceil(3.5); // 4.0
Math.ceil(3.9); // 4.0

Math.ceil(-3.0); // -3.0
Math.ceil(-3.1); // -3.0
Math.ceil(-3.5); // -3.0
Math.ceil(-3.9); // -3.0

пол

любое десятичное значение округляется до следующее целое число. Этот метод возвращает значение double.

Math.floor(3.0); // 3.0
Math.floor(3.1); // 3.0
Math.floor(3.5); // 3.0
Math.floor(3.9); // 3.0

Math.floor(-3.0); // -3.0
Math.floor(-3.1); // -4.0
Math.floor(-3.5); // -4.0
Math.floor(-3.9); // -4.0

Ринт

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

Math.rint(3.0); // 3.0
Math.rint(3.1); // 3.0
Math.rint(3.5); // 4.0 ***
Math.rint(3.9); // 4.0
Math.rint(4.5); // 4.0 ***
Math.rint(5.5); // 6.0 ***

Math.rint(-3.0); // -3.0
Math.rint(-3.1); // -3.0
Math.rint(-3.5); // -4.0 ***
Math.rint(-3.9); // -4.0
Math.rint(-4.5); // -4.0 ***
Math.rint(-5.5); // -6.0 ***

Я согласен с выбранным ответом на использование DecimalFormat --- или BigDecimal.

читайте обновление ниже первого!

если вы do хотите округлить двойное значение и получить double значение результата, вы можете использовать org.apache.commons.math3.util.Precision.round(..) как уже упоминалось выше. Реализация использует BigDecimal, медленно и создает мусор.

аналогичный, но быстрый и свободный от мусора метод предоставляется DoubleRounder утилиты в библиотеке decimal4j:

 double a = DoubleRounder.round(2.0/3.0, 3);
 double b = DoubleRounder.round(2.0/3.0, 3, RoundingMode.DOWN);
 double c = DoubleRounder.round(1000.0d, 17);
 double d = DoubleRounder.round(90080070060.1d, 9);
 System.out.println(a);
 System.out.println(b);
 System.out.println(c);
 System.out.println(d);

выводит

 0.667
 0.666
 1000.0
 9.00800700601E10

посмотреть https://github.com/tools4j/decimal4j/wiki/DoubleRounder-Utility

отказ от ответственности: я участвую в проекте decimal4j.

обновление: Как отметил @iaforek, DoubleRounder иногда возвращает противоречивые результаты. Причина в том, что он выполняет математически правильным округлением. Например,DoubleRounder.round(256.025d, 2) будет округление до 256.02, поскольку двойное значение, представленное как 256.025 d, несколько меньше рационального значения 256.025 и, следовательно, будет округлено вниз.

Примечания:

  • это поведение очень похоже на поведение BigDecimal(double) конструктор (но не valueOf(double) который использует строковый конструктор).
  • проблему можно обойти с помощью двойного шага округления до более высокой точности, но это сложно, и я не являюсь вдаваясь в подробности здесь

по этим причинам и все, что упоминалось выше в этом посте я не могу рекомендовать использовать DoubleRounder.

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

Math.round(selfEvaluate*100000d.0)/100000d.0;

или

Math.round(selfEvaluate*100000d.0)*0.00000d1;

Если вам нужно большое значение десятичных знаков, вы можете использовать BigDecimal вместо этого. В любом случае .0 важно. Без него округление 0. 33333d5 возвращает 0.333333 и позволяет только 9 цифр. Вторая функция без .0 имеет проблемы с возвратом 0.30000 0,30000000000000004.

для этого мы можем использовать этот форматер:

 DecimalFormat df = new DecimalFormat("#.00");
 String resultado = df.format(valor)

или:

DecimalFormat df = new DecimalFormat("0.00"); :

используйте этот метод, чтобы получить всегда два десятичных знака:

   private static String getTwoDecimals(double value){
      DecimalFormat df = new DecimalFormat("0.00"); 
      return df.format(value);
    }

при определении этого значения:

91.32
5.22
11.5
1.2
2.6

используя метод, мы можем получить такой результат:

91.32
5.22
11.50
1.20
2.60

демо онлайн.

имейте в виду, что строку.format () и DecimalFormat создают строку, используя Локаль по умолчанию. Так они могут написать отформатированное число с точкой или запятой в качестве разделителя между целой и дробной частей. Чтобы убедиться, что округленная строка находится в формате, который вы хотите использовать java.текст.NumberFormat как так:

  Locale locale = Locale.ENGLISH;
  NumberFormat nf = NumberFormat.getNumberInstance(locale);
  // for trailing zeros:
  nf.setMinimumFractionDigits(2);
  // round to 2 digits:
  nf.setMaximumFractionDigits(2);

  System.out.println(nf.format(.99));
  System.out.println(nf.format(123.567));
  System.out.println(nf.format(123.0));

будет печатать на английском языке (независимо от того, что ваш язык): 0.99 123.57 123.00

пример взят из Farenda -как конвертировать double в Строка правильно.

здесь dp = десятичное число, которое вы хотите, и стоимостью - это двойной.

    double p = Math.pow(10d, dp);

    double result = Math.round(value * p)/p;

Если вы считаете 5 или n число десятичных. Может быть, этот ответ решит вашу проблему.

    double a = 123.00449;
    double roundOff1 = Math.round(a*10000)/10000.00;
    double roundOff2 = Math.round(roundOff1*1000)/1000.00;
    double roundOff = Math.round(roundOff2*100)/100.00;

    System.out.println("result:"+roundOff);

выход будет: 123.01
это можно решить с помощью цикла и рекурсивной функции.

Если вы используете технологию, которая имеет минимальный JDK. Вот способ без каких-либо Java libs:

double scale = 100000;    
double myVal = 0.912385;
double rounded = (int)((myVal * scale) + 0.5d) / scale;

В общем случае округление выполняется путем масштабирования:round(num / p) * p

/**
 * MidpointRounding away from zero ('arithmetic' rounding)
 * Uses a half-epsilon for correction. (This offsets IEEE-754
 * half-to-even rounding that was applied at the edge cases).
 */
double RoundCorrect(double num, int precision) {
    double c = 0.5 * EPSILON * num;
//  double p = Math.pow(10, precision); //slow
    double p = 1; while (precision--> 0) p *= 10;
    if (num < 0)
        p *= -1;
    return Math.round((num + c) * p) / p;
}

// testing edge cases
RoundCorrect(1.005, 2);   // 1.01 correct
RoundCorrect(2.175, 2);   // 2.18 correct
RoundCorrect(5.015, 2);   // 5.02 correct

RoundCorrect(-1.005, 2);  // -1.01 correct
RoundCorrect(-2.175, 2);  // -2.18 correct
RoundCorrect(-5.015, 2);  // -5.02 correct