14

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

16

У меня есть задача: мне нужен метод для преобразования числа с плавающей запятой (double) в строку, который использует метод округления "вверх при 5" — то есть, если цифра, которую нужно округлить, равна 5, она всегда округляется вверх до следующего целого числа. Это стандартный способ округления, который многие ожидают в большинстве ситуаций.

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

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

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

Это возвращает:

0.91239

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

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

Это возвращает:

0.91230

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

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

Это возвращает:

0.91238

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

0.912385 -> 0.91239
0.912300 -> 0.9123

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

4 ответ(ов)

5

Предположим, что value — это тип double. Вы можете сделать так:

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

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

2

Чтобы получить объект BigDecimal из значения типа double, можно использовать следующий код:

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

После этого, чтобы получить строку из этого объекта 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));
    }
}

В этом примере создается объект BigDecimal, в который помещается результат вычисления, и масштабируем его до 10 знаков после запятой, используя режим округления ROUND_HALF_UP.

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

ПРИМЕЧАНИЕ: Чтобы ответить на некоторые комментарии ниже, я переделал часть тестового цикла с модулем, используя 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
0

Вы можете использовать класс DecimalFormat для форматирования числа с двумя десятичными знаками. Вот пример кода на Java:

double d = 3.76628729;

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

В этом примере мы создаем объект DecimalFormat, указывая шаблон форматирования "#.##", который позволяет отображать число с двумя знаками после десятичной точки. После этого мы форматируем число d и возвращаем его в виде объекта типа Double.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь