Как округлить число до 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 ]

684 голосов
/ 30 сентября 2008

Используйте 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
436 голосов
/ 30 сентября 2008

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

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

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

177 голосов
/ 30 сентября 2008
new BigDecimal(String.valueOf(double)).setScale(yourScale, BigDecimal.ROUND_HALF_UP);

даст вам BigDecimal. Чтобы извлечь из него строку, просто вызовите этот метод BigDecimal 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));
    }
108 голосов
/ 30 сентября 2008

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

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

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

77 голосов
/ 02 октября 2012

Как отметили некоторые другие, правильный ответ - использовать либо 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

РЕДАКТИРОВАТЬ: Чтобы ответить на некоторые комментарии ниже, я переделал часть модуля тестового цикла, используя 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
76 голосов
/ 28 января 2011

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

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

55 голосов
/ 29 сентября 2011

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

double d = 3.76628729;

DecimalFormat newFormat = new DecimalFormat("#.##");
double twoDecimal =  Double.valueOf(newFormat.format(d));
34 голосов
/ 01 сентября 2009

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

BigDecimal bd = new BigDecimal(Double.toString(d));
bd = bd.setScale(decimalPlace, BigDecimal.ROUND_HALF_UP);
return bd.doubleValue();
31 голосов
/ 30 сентября 2008
double myNum = .912385;
int precision = 10000; //keep 4 digits
myNum= Math.floor(myNum * precision +.5)/precision;
29 голосов
/ 03 июля 2011

@ Мильхос: превосходный десятичный формат для округления:

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

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

[РЕДАКТИРОВАТЬ: также, если формат каретки похож на ("# 0. ############") и вы введите десятичную дробь, например 3.1415926, ради аргумента, DecimalFormat не производит никакого мусора (например, конечные нули) и возвращает: 3.1415926 .. если ты так склонен. Конечно, это немного многословно по вкусу некоторым разработчикам - но эй, у него мало памяти во время обработки и очень прост в реализации.]

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

...